2

I have a problem overloading an extension method.

I have two extension Methods:

Method A - For standard objects:

public static bool HasChanged<T>(this T obj1, T obj2, Func<T, T, bool> equalityExpression)

Method B - For IEnumerables:

public static bool HasChangedList<T>(this IEnumerable<T> obj1, IEnumerable<T> obj2, Func<T, T, bool> isEqualExpression)

But I would like to give them both the same names, that is currently not working, cause IEnumerables are objects aswell, so the compiler isnt able to decide whether to use the first one or the second one on an IEnumerable.

I am sure, its not possible to let first method take all object but an IEnumerable, so is there another way around?

Jannik
  • 2,310
  • 6
  • 32
  • 61
  • 2
    You could check in the body of the first if it is IEnumerable and call a second private method, then you only need one. – James Dec 30 '14 at 11:52
  • 1
    Assigning the same names on both methods works perfectly fine for me. – Jan Köhler Dec 30 '14 at 11:58
  • 1
    You said *that is currently not working*. What does that mean? What is the error you get? or unexpected behavior or what? – Sriram Sakthivel Dec 30 '14 at 11:58
  • I am so sorry, I think made a mistake, it is working indeed, Visual Studios IntelliSense made me think it's not working. – Jannik Jan 02 '15 at 07:58

1 Answers1

4

(Not really a solution, but too long for a comment. Hopefully, one of the C# spec gurus will show up and tell us why overload resolution works like this in this particular case.)

If

  • you qualify the parameters of your equalityExpression or if
  • the inner type of the IEnumerable can be inferred from the lambda expression,

it should work fine:

class Program
{
    static void Main(string[] args)
    {
        var array = new[] { 1, 2, 3 };

        // uses the IEnumerable overload -- prints false
        Console.WriteLine(array.HasChanged(array, (int x, int y) => x == y));

        // uses the IEnumerable overload -- prints false
        Console.WriteLine(array.HasChanged(array, (x, y) => x >= y));

        // uses the generic overload -- prints true
        Console.WriteLine(array.HasChanged(array, (x, y) => x == y));

        Console.ReadLine();
    }
}
static class Extensions
{
    public static bool HasChanged<T>(this IEnumerable<T> obj1, IEnumerable<T> obj2, Func<T, T, bool> isEqualExpression)
    { 
        return false; 
    }
    public static bool HasChanged<T>(this T obj1, T obj2, Func<T, T, bool> equalityExpression)
    { 
        return true; 
    }
}
Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • That's definitely an unexpected behavior you found there. – Andrew Dec 30 '14 at 12:27
  • @Andrew: Indeed, I would have expected an overload resolution ambiguity error in the last case. – Heinzi Dec 30 '14 at 12:27
  • 1
    If `array` is an *array* or a `List`, it works as you specified. But if in any of those cases, the variable is declared as `IEnumerable` instead of using `var` or `List`, you get the ambiguity error. This is getting really confusing. – Andrew Dec 30 '14 at 12:42
  • that's not unexpected – Alexander Leyva Caro Dec 30 '14 at 13:25
  • when you specify var the compiler assume the type is an int[] and the best overload is the generic, when you push IEnumerable is ambiguous because it can use both overloads. When you specify the Func then it decide by the IEnumerable overload. – Alexander Leyva Caro Dec 30 '14 at 13:30
  • I assume that, in the last case, the generic overload is preferred, since no widening conversion `int[] -> IEnumerable` is required: `int[]` can be used directly as `T`. – Heinzi Dec 30 '14 at 15:08