views:

640

answers:

4

Consider the following class,

class Foo
{
    public Foo(int count)
    {
        /* .. */
    }

    public Foo(int count)
    {
        /* .. */
    }
}

Above code is invalid and won't compile. Now consider the following code,

class Foo<T>
{
    public Foo(int count)
    {
        /* .. */
    }

    public Foo(T t)
    {
        /* .. */
    }
}

static void Main(string[] args)
{
    Foo<int> foo = new Foo<int>(1);
}

Above code is valid and compiles well. It calls Foo(int count).

My question is, if the first one is invalid, how can the second one be valid? I know class Foo<T> is valid because T and int are different types. But when it is used like Foo<int> foo = new Foo<int>(1), T is getting integer type and both constructor will have same signature right? Why don't compiler show error rather than choosing an overload to execute?

+22  A: 

There is no ambiguity, because the compiler will choose the most specific overload of Foo(...) that matches. Since a method with a generic type parameter is considered less specific than a corresponding non-generic method, Foo(T) is therefore less specific than Foo(int) when T == int. Accordingly, you are invoking the Foo(int) overload.

Your first case (with two Foo(int) definitions) is an error because the compiler will allow only one definition of a method with precisely the same signature, and you have two.

John Feminella
I checked the language specification, but it doesn't say generic parameters are less specific than concrete types. Non-generic methods are considered 'better' than generic methods however.
Thorarin
You're right, that wasn't phrased very well. I meant to say "a method with a generic type parameter...".
John Feminella
BTW, for the curious, I think the section Thorarin is talking about is §14.4.2.2, which defines "better function member".
John Feminella
I understand this. But IMO, compiler should warn as this type of behavior makes confusion.
Appu
Not sure how the compiler could do anything useful here by way of a warning - as soon as you defined Foo(T t) - all other single parameter methods with that name would fall foul of the sort of check you suggest.
Gordon Mackie JoanMiro
It doesn't warn about this kind of behavior because it is exactly the intention of the language author that it be used this way. The generic path is used for any data type the class instantiator wants, but when they instantiate the class with an int as the generic type the class has been given a special function that is assumed to be optimized or otherwise tailored for the int type. This is a very basic part of the language, and thus not an error.
Michael Speer
I agree. This is no more "confusing" than, say, "object o = 5". Should the compiler warn you every time it's taking advantage of an implicit but unambiguous assumption to make your life easier?
John Feminella
+9  A: 

Eric Lippert blogged about this recently.

Mike Two
Thank you, I was looking for that link.
Brian Rasmussen
And, apparently, he's answered the question here as well.
Mike Two
A: 

The fact is that they do not both have the same signature - one is using generics while this other is not.

With those methods in place you could also call it using a non-int object:

Foo<string> foo = new Foo<string>("Hello World");
DilbertDave
+11  A: 

Your question was hotly debated when C# 2.0 and the generic type system in the CLR were being designed. So hotly, in fact, that the "bound" C# 2.0 specification published by A-W actually has the wrong rule in it! There are four possibilities:

1) Make it illegal to declare a generic class that could POSSIBLY be ambiguous under SOME construction. (This is what the bound spec incorrectly says is the rule.) So your Foo<T> declaration would be illegal.

2) Make it illegal to construct a generic class in a manner which creates an ambiguity. declaring Foo<T> would be legal, constructing Foo<double> would be legal, but constructing Foo<int> would be illegal.

3) Make it all legal and use overload resolution tricks to work out whether the generic or nongeneric version is better. (This is what C# actually does.)

4) Do something else I haven't thought of.

Rule #1 is a bad idea because it makes some very common and harmless scenarios impossible. Consider for example:

class C<T>
{
  public C(T t) { ... } // construct a C that wraps a T
  public C(Stream state) { ... } // construct a C based on some serialized state from disk
}

You want that to be illegal just because C<Stream> is ambiguous? Yuck. Rule #1 is a bad idea, so we scrapped it.

Unfortunately, it is not as simple as that. IIRC the CLI rules say that an implementation is allowed to reject as illegal constructions that actually do cause signature ambiguities. That is, the CLI rules are something like Rule #2, whereas C# actually implements Rule #3. Which means that there could in theory be legal C# programs that translate into illegal code, which is deeply unfortunate.

For some more thoughts on how these sorts of ambiguities make our lives wretched, here are a couple of articles I wrote on the subject:

http://blogs.msdn.com/ericlippert/archive/2006/04/05/569085.aspx

http://blogs.msdn.com/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx

Eric Lippert
Guess the third Foo<> at 2 has to be int
Dykam
Thanks for the answer Eric.
Appu