Creating an IEnumerables Type Wrap Extension Method

I wanted to create an extension method that would effectively wrap individual objects as IEnumerables. This was to avoid cases where you insert new [] {}

in the middle of an expression. It's easy enough to do it using the following method:

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

      

The problem is that this will apply to all types (which is the expected behavior), but it will also have the side effect of using the method on instances IEnumerable <T>

. In case the allowed extended type is IEnumerable<T>

, I would just like to return this IEnumerable, since the alternate lookup is in IEnumerable<IEnumerable<T>>

, which is not actually what you expected when the method was called.

Instinctively (and possibly sleepily), I first created an overload that looked like this:

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

      

to handle the case where the wrap type is IEnumerable<T>

, but of course control flow always allows the first method.

So the question is, how could I create a wrapper method like this that handles as a case when an extended parameter instance is IEnumerable<T>

and when it isn't?

+3


source to share


2 answers


Here's another attempt, inspired by Eric Lippert a great message: fooobar.com/questions/107301 / ... .

You can control overload resolution by placing your 2 extension methods at different levels of the namespace hierarchy.

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

      



However, the downside is that you always need to refer to both namespaces to get the correct method invocation behavior.

using MyExtensions;
using MyExtensions.LowerPrecedenceNamespace;

      

+3


source


Do you think you've changed your signature a bit makes the code a little more complicated, but makes it extremely simple.



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

      

+1


source







All Articles