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

时间:2015-06-03 19:50:08

标签: c# generics extension-methods ienumerable

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

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

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

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

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

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

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

2 个答案:

答案 0 :(得分:3)

这是另一个尝试,灵感来自Eric Lippert在https://stackoverflow.com/a/1451184/4955425的优秀帖子。

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

using MyExtensions;
using MyExtensions.LowerPrecedenceNamespace;

但是,缺点是你总是需要引用两个命名空间来获得正确的方法调用行为。

DB.Run()

答案 1 :(得分:1)

您是否考虑过更改签名,使代码更复杂但使用非常简单。

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" });
    }
}