简体   繁体   中英

Why doesn't the following generic method call require a type?

Sorry - not sure of a better name, please modify if you can think of better.

I am trying to learn a bit more about IEnumerable/collections/generics and I thought I was getting somewhere, until this example got me:

 var nums = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10 };

 var result = FilterNums(nums, i => i % 2 != 0);

.....

public static IEnumerable<T1> FilterNums<T1>(IEnumerable<T1> numslist, Func<T1, bool> predicateDelegate)

.....

Why does the call the call to FilterNums work? If I change it to FilterNums<int> , it still works and that is what I actually expected to type.

So, is it some how detecting the T1 for the lambda query and not requiring me to write it or is something else going on?

The type inference process is a bit complicated; see chapter 7 of the C# spec for exact details.

Briefly it works like this.

When you invoke a method without a generic parameter list, first we make a set of all the accessible methods of that name.

Next we check to see if any of them are generic. If they are then we try to see if the generic type arguments can be inferred from the actual arguments. The inference process goes like this. Suppose you have an argument list A:

A: (nums, i => i%2 != 0)

and a list of formal parameter types P:

P: (IEnumerable<T1>, Func<T1, bool>)

and a set of generic type parameters X:

X: <T1>

The goal of type inference is to make inferences from every member of A to the corresponding member of P in order to deduce enough information about each member of X.

This particular problem is easy. From the first argument we see that the type of nums is int[]. We see that the first formal parameter in P is IEnumerable<T1> . We know that int[] is convertible to IEnumerable<int> , and therefore T1 might be int. We make a note of that fact.

And at this point, we're basically done. We have nothing about T1 that we can infer from the second argument/parameter pair. Type inference succeeds, and determines that T1 is int. So we pretend that you called it with <int> as the type argument list.

That was a very simple type inference problem. Consider this one:

A: (customers, c=>c.Name)
P: (IEnumerable<T>, Func<T, R>)
X: <T, R>

This is the problem you get when you do customers.Select(c=>c.Name) .

What do we do? From the first argument we deduce "customers implements IEnumerable<Customer> , therefore T is probably Customer". After making that deduction we can then say "c in the lambda is therefore Customer. Therefore this is a lambda from Customer to whatever the type of Customer.Name is. That's string. Therefore R is string".

See how in that case one inference had to be chained onto the next; we cannot simply do inference "in parallel" because one inference might depend on the results of another. Those dependencies can contain loops and other odd topologies. The exact details of how we make progress through that chain of dependencies is a bit complicated; see the spec for details.

We also have to deal with the case where two or more bounds are deduced for a type parameter, and whether those bounds are "upper", "lower" or "exact".

If this subject interests you, I've written about it extensively. See

http://blogs.msdn.com/b/ericlippert/archive/tags/type+inference/

for lots of articles on various aspects of type inference, not just generic method type inference. For a video of me explaining how method type inference works in C# 3.0, see:

http://wm.microsoft.com/ms/msdn/visualcsharp/eric_lippert_2006_11/EricLippert01.wmv

This is because of type inference.

The compiler understands that you're sending an int, so it implicitly sets the generic type to int. If you'll look in the intellisense you'll see your generic type is set as int.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM