3

I have an extension method that works on any class, but I want to call a special version if I am working on IEnumerable<T>.

For Example

public static class ExtensionMethods
{

    public static dynamic Test<T>(this T source)
    {   
        dynamic expandoObject = new System.Dynamic.ExpandoObject();
        var dictionary = (IDictionary<string,object>)expandoObject;

        dictionary["Test"] = source.ToString();

        return dictionary;
    }

    public static IEnumerable<dynamic> Test<T>(this List<T> source)
    {
        var result = new List<dynamic>();
        foreach(var r in source)
            yield return r.Test();          
    }


    public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source)
    {
        var result = new List<dynamic>();
        foreach(var r in source)
            yield return r.Test();          
    }
}   

// Usage

public class X 
{
    string guid = Guid.NewGuid().ToString();
}


void Main()
{
    List<X> list = new List<X>() { new X() };

    list.Test().Dump();                     // Correct but only works because there is an explicit overload for List<T>

    var array = list.ToArray();
    ((IEnumerable<X>) array).Test().Dump(); // Correct

     array.Test().Dump(); // Calls the wrong extension method
}

Is there any way I can get array.Test() to call the IEnumerable version without having to explicitly cast it?

Alternatively, if I give the extension method different names, if there any way I can get a compiler error if I accidently use the wrong one?

sgmoore
  • 15,694
  • 5
  • 43
  • 67

3 Answers3

1

I think you are trying to solve it in a wrong direction. The List implements IEnumerable interface and as such the compiler can have problem with solving the best method will be invoked on List. What you could do -- you could test if the IEnumerable is a list inside the extension method.

public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source)
{
    if (source is List<T>) {
        // here 
    }
    var result = new List<dynamic>();
    foreach(var r in source)
        yield return r.Test();          
}
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • I don't want a different version for List vs IEnumerable. I just added the List version to show that if I create an explicit overload for List it will be correctly called. If I created an explicit overload for T[] it would work for arrays, but I can't create an explicit overload for every possible class that implements IEnumerable. – sgmoore Mar 07 '15 at 12:58
  • Sorry of course I meant you could test the source for being an array `if (sourse is T[])` – W.F. Mar 07 '15 at 13:01
  • Still misses the point that the IEnumerable extension method is not called . – sgmoore Mar 07 '15 at 14:09
0

You can specify T and not rely on type inference, this will hint compiler to use correct extension method. Code would look like this:

var array = list.ToArray();
array.Test<X>().Dump();

What happens is, that compiler cannot tell which extension to use, since Array is valid argument for both method signatures:

public static dynamic Test<T>(this T source) { .. }

public static IEnumerable<dynamic> Test<T>(this IEnumerable<T> source) { .. }

In first case compiler can assume T is of type Array. Because of it, compiler has to picks one (might be first defined?).

Marian Polacek
  • 2,906
  • 4
  • 30
  • 37
  • That would work, but it doesn't force T to be specified and hence will still compile incorrect code. – sgmoore Mar 07 '15 at 14:02
  • You are correct, sadly I don't know if compile it is possible to achieve compile time error in this case. But you can add another extension method, like mentioned as mentioned in answer below. – Marian Polacek Mar 07 '15 at 15:28
0

Add this extension method to explicitly catch all array types:

public static IEnumerable<dynamic> Test<T>(this T[] source)
{
    var result = new List<dynamic>();
    foreach(var r in source)
        yield return r.Test();          
}
Pieter Geerkens
  • 11,775
  • 2
  • 32
  • 52