Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the method overload resolution system decide which method to call when a null value is passed?

So for instance you have a type like:

public class EffectOptions
{
    public EffectOptions ( params object [ ] options ) {}

    public EffectOptions ( IEnumerable<object> options ) {}

    public EffectOptions ( string name ) {}

    public EffectOptions ( object owner ) {}

    public EffectOptions ( int count ) {}

    public EffectOptions ( Point point ) {}

}

Here I just give the example using constructors but the result will be the same if they were non-constructor methods on the type itself, right?

So when you do:

EffectOptions options = new EffectOptions (null);

which constructor would be called, and why?

I could test this myself but I want to understand how the overload resolution system works (not sure if that's what it's called).

like image 879
Joan Venge Avatar asked Mar 02 '11 20:03

Joan Venge


2 Answers

For the exact rules, see the overload resolution spec. But briefly, it goes like this.

First, make a list of all the accessible constructors.

public EffectOptions ( params object [ ] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 
public EffectOptions ( int count ) 
public EffectOptions ( Point point )

Next, eliminate all the inapplicable constructors. An applicable constructor is one where every formal parameter has a corresponding argument, and the argument is implicitly convertible to the formal parameter type. Assuming that Point is a value type, we eliminate the "int" and "Point" versions. That leaves

public EffectOptions ( params object[] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 

Now, we have to consider whether the one with "params" is applicable in its expanded or unexpanded form. In this case it is applicable in both forms. When that happens, we discard the expanded form. So that leaves

public EffectOptions ( object[] options )
public EffectOptions ( IEnumerable<object> options ) 
public EffectOptions ( string name )
public EffectOptions ( object owner ) 

Now we must determine the best of the applicable candidates. The bestness rules are complicated, but the short version is that more specific is better than less specific. Giraffe is more specific than Mammal, Mammal is more specific than Animal, Animal is more specific than object.

The object version is less specific than all of them, so it can be eliminated. The IEnumerable<object> version is less specific than the object[] version (do you see why?) so it can be eliminated too. That leaves

public EffectOptions ( object[] options )
public EffectOptions ( string name )

And now we are stuck. object[] is neither more nor less specific than string. Therefore this gives an ambiguity error.

That is just a brief sketch; the real tiebreaking algorithm is much more complicated. But those are the basics.

like image 89
Eric Lippert Avatar answered Oct 25 '22 07:10

Eric Lippert


In this case the C# compiler won't pick any constructor and instead will error. The value null is legal for several of the available constructors and there is insufficient tie breaking logic to pick one hence it produces an error.

The C# compiler overload resolution logic is a complex process but a short (and inherently incomplete) overview of how it works is as follows

  • Collect all members with the given Name
  • Filter the members to those with parameter lists that are compatible with the provided arguments and with the appropriate accessibility
  • If the remaining members has more than one element, employ tie breaking logic to choose the best of them

The full details are listed in section 7.4 of the C# language spec. And I'm sure Eric will be along shortly to give a much more accurate description :)

like image 23
JaredPar Avatar answered Oct 25 '22 08:10

JaredPar