Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# Generic overloaded method dispatching ambiguous

I just hit a situation where a method dispatch was ambiguous and wondered if anyone could explain on what basis the compiler (.NET 4.0.30319) chooses what overload to call

interface IfaceA
{

}

interface IfaceB<T>
{
    void Add(IfaceA a);
    T Add(T t);
}

class ConcreteA : IfaceA
{

}

class abstract BaseClassB<T> : IfaceB<T>
{
    public virtual T Add(T t) { ... }
    public virtual void Add(IfaceA a) { ... }
}

class ConcreteB : BaseClassB<IfaceA>
{
    // does not override one of the relevant methods
}

void code()  
{
    var concreteB = new ConcreteB();

    // it will call void Add(IfaceA a)
    concreteB.Add(new ConcreteA());
}

In any case, why does the compiler not warn me or even why does it compile? Thank you very much for any answers.

like image 808
Sebastian Avatar asked May 18 '10 07:05

Sebastian


2 Answers

It follows the rules in section 7.5.3.2 of the C# 4 specification ("Better function member").

First (well, after seeing that both methods are applicable) we need to check the conversions from argument types to parameter types. In this case it's reasonably simple because there's only one argument. Neither conversion of argument type to parameter type is "better" because both are converting from ConcreteA to IfaceA. It therefore moves on to the next set of criteria, including this:

Otherwise, if MP has more specific parameter types than MQ, then MP is better than MQ. Let {R1, R2, …, RN} and {S1, S2, …, SN} represent the uninstantiated and unexpanded parameter types of MP and MQ. MP’s parameter types are more specific than MQ’s if, for each parameter, RX is not less specific than SX, and, for at least one parameter, RX is more specific than SX:specific than SX:

  • A type parameter is less specific than a non-type parameter.
  • ...

So even though the conversion is equally good, the overload using IfaceA directly (rather than via delegates) is deemed "better" because a parameter of type IfaceA is more specific than a parameter of type T.

There's no way of getting the compiler to warn on this behaviour - it's just normal overload resolution.

like image 157
Jon Skeet Avatar answered Sep 23 '22 15:09

Jon Skeet


Because the compiler chooses the most specific first.

What happens if you call like so:

void code()   
{ 
    var concreteB = new ConcreteB(); 

    IfaceA  x = concreteB.Add(new ConcreteA()); 
} 
like image 24
Mitch Wheat Avatar answered Sep 22 '22 15:09

Mitch Wheat