views:

401

answers:

7

Why does this work? I'm not complaining, just want to know.

void Test()
{    
    int a = 1;
    int b = 2;

    What<int>(a, b);
    // Why does this next line work?
    What(a, b);
}

void What<T>(T a, T b)
{

}
+7  A: 

The C# compiler supports type inference for generics, and also commonly seen if you are using the var keyword.

Here int is inferred from the context (a and b) and so <int> is not needed. It keeps code cleaner and easier to read at times.

Sometimes your code may be more clear to read if you let the compiler infer the type, sometimes it may be more clear if you explicitly specify the type. It is a judgement call on your given situation.

Brian R. Bondy
He isn't using the `var` keyword.
SLaks
@SLaks: Yes I know.
Brian R. Bondy
@SLaks: "'also' commonly seen"
Dykam
@Dykam: He added that phrase later.
SLaks
@Dykam: It read The C# compiler supports type inference for generics when you use generics and when you use the var keyword. I clarified more specifically so there was no room for confusion on what I meant. I.e. I meant "and also in this situation" and @SLaks thought I meant "and also at the same time as".
Brian R. Bondy
@SLaks, ah... don't see an edit.
Dykam
@Dykam: All edits made within five minutes of each-other are combined. Since he edited it less than five minutes after giving the answer, the edit left no trace.
SLaks
+5  A: 

The compiler infers the generic type parameter from the types of the actual parameters that you passed.

This feature makes LINQ calls much simpler. (You don't need to write numbers.Select<int, string>(i => i.ToString()), because the compiler infers the int from numbers and the string from ToString)

SLaks
+17  A: 

It works because a and b are integers, so the compiler can infer the generic type argument for What.

In C# 3, the compiler can also infer the type argument even when the types don't match, as long as a widening conversion makes sense. For instance, if c were a long, then What(a, c) would be interpreted as What<long>.

Note that if, say, c were a string, it wouldn't work.

Dan Tao
Note also that if you have two different types which both extend `Parent` and you try to do `What(a, b)` (where a and b are of those two different types), it won't know to infer the type as `Parent`.
BlueRaja - Danny Pflughoeft
@BlueRaja: that is actually the same case, since in Dan's example int and string are both extensions of Object. The design principle here is that when asked to find a "best" member of a set, C# always picks a member from the set. It never picks a member from outside the set. If asked what the best member is of {Tiger, Giraffe} it doesn't say "Animal", it says that there is no best member.
Eric Lippert
+1  A: 

The compiler is smart enough to figure out that the generic type is 'int'

dewald
+7  A: 

It's using type inference for generic methods. Note that this has changed between C# 2 and 3. For example, this wouldn't have worked in C# 2:

What("hello", new object());

... whereas it would in C# 3 (or 4). In C# 2, the type inference was performed on a per-argument basis, and the results had to match exactly. In C# 3, each argument contributes information which is then put together to infer the type arguments. C# 3 also supports multi-phase type inference where the compiler can work out one type argument, then see if it's got any more information on the rest (e.g. due to lambda expressions with implicit parameter types). Basically it keeps going until it can't get any more information, or it finishes - or it sees contradictory information. The type inference in C# isn't as powerful as the Hindley-Milner algorithm, but it works better in other ways (in particular it always makes forward progress).

See section 7.4.2 of the C# 3 spec for more information.

Jon Skeet
+2  A: 

The compiler can infer type T to be an int since both parameters passed into What() are of type int. You'll notice a lot of the Linq extensions are defined with generics (as IEnumerable) but are typically used in the manner you show.

Emil Lerch
+2  A: 

If the subject of how this works in C# 3.0 is interesting to you, here's a little video of me explaining it from back in 2006, when we were first designing the version of the feature for C# 3.0.

http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx

See also the "type inference" section of my blog:

http://blogs.msdn.com/ericlippert/archive/tags/Type+Inference/default.aspx

Eric Lippert