创建扩展方法以将类型包装为 IEnumerables

Creating an extension method to wrap types as IEnumerables

我想创建一个扩展方法来有效地将单个对象包装为 IEnumerables。这是为了避免最终将 new [] {} 放在表达式中间的情况。使用以下方法很容易做到这一点:

public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
{
    return new[] { source };
}

问题是这将应用于任何和所有类型(这是预期的行为),但这也会产生使该方法在 IEnumerable <T> 实例上可用的副作用。在解析的扩展类型是 IEnumerable<T> 的情况下,我只想 return 这个 IEnumerable,因为替代方案是发现自己有一个 IEnumerable<IEnumerable<T>>,这不是你真正想要的d expect 调用方法时。

本能地(也许是困倦),我首先创建了一个看起来像这样的重载

public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}

为了处理要包装的类型是 IEnumerable<T> 的情况,但是当然,控制流总是解析到第一个方法。

所以,问题是:我如何创建这样一个包装方法来处理扩展参数实例是 IEnumerable<T> 和不是 IEnumerable<T> 的情况?

这是另一个尝试,灵感来自埃里克·利珀特 (Eric Lippert) 出色的 post,网址为:。

您可以通过将 2 个扩展方法放在命名空间层次结构中的不同级别来控制重载分辨率。

namespace MyExtensions
{
    public static class HighPrecendenceExtensions
    {
        public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this IEnumerable<TSource> source)
        {
            return source;
        }
    }

    namespace LowerPrecedenceNamespace
    {
        public static class LowPrecedenceExtensions
        {
            public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this TSource source)
            {
                return new[] { source };
            }
        }
    }
}

但是,缺点是您总是需要引用两个名称空间才能获得正确的方法调用行为。

using MyExtensions;
using MyExtensions.LowerPrecedenceNamespace;

你有没有考虑过稍微改变一下你的签名,让代码更复杂一点,但使用起来却非常简单。

public static class Extensions
{
    public static IEnumerable<TSource> WrapAsEnumerable<TSource>(this object source)
    {
        var allInterfaces = source.GetType().GetInterfaces();

        IEnumerable<Type> allEnumerableInterfaces = allInterfaces.Where(t => t.Name.StartsWith("IEnumerable"));
        if (!allEnumerableInterfaces.Any())
            return new[] { (TSource)source };

        IEnumerable<Type> genericEnumerableOfTSource = allEnumerableInterfaces.Where(t => t.GenericTypeArguments.Contains(typeof(TSource)));

        // we have a generic implementation
        if (genericEnumerableOfTSource.Any())
        {
            return (IEnumerable<TSource>) source;
        }

        return new[] { (TSource)source }; ;
    }
}

[TestFixture]
public class Tests
{
    [Test]
    public void should_return_string_an_enumerable()
    {
        const string aString = "Hello World";
        var wrapped = aString.WrapAsEnumerable<string>();
        wrapped.ShouldAllBeEquivalentTo(new[] {"Hello World"});
    }

    [Test]
    public void should_return_generic_enumerable_unwrapped()
    {
        var aStringList = new List<string> { "Hello", "World" };
        var wrapped = aStringList.WrapAsEnumerable<string>();
        wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
    }

    [Test]
    public void should_return_non_generic_enumerable_unwrapped()
    {
        var aStringArray = new[] {"Hello", "World"};
        var wrapped = aStringArray.WrapAsEnumerable<string>();
        wrapped.ShouldAllBeEquivalentTo(new[] { "Hello", "World" });
    }
}