简体   繁体   中英

Generic type inference

I'm pretty comfortable with generics but being a person that likes to understand every detail I have this question. In my implementation of the Where LINQ extension method

    public static class Extensions
    {
        public static IEnumerable<T> Where<T>(
                         this IEnumerable<T> source, Func<T, bool> predicate)
        {
            foreach (T element in source)
                if (predicate(element))
                    yield return element;
        }
    }

why is the T in Where<T> necessary? Why can't the type be inferred from the T in IEnumerable<T> ? In other words, why can't the signature be

public static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)

Thanks for any help.

public static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)

With such a signature, T would be an undeclared identifier. You cannot just use an IEnumerable<T> if T is not defined anywhere. Therefore, you need a generic parameter T on the method (or alternatively on the class) level for IEnumerable<T> to make sense.

There is no reason at all that the T in IEnumerable<T> and Where<T> have to be the same thing; you are saying they must be by specifying them both as T

IEnumerable<T> is what is being returned by the method. Where<T> is for defining what the generic type used as the parameters is. It is conceivably possible those T could be different types, if you had not specified them both as T

That is: The return type ( IEnumerable<T> ) is not necessarily related to the types of the parameters (the T in the Where<T> ).

The <T> prefacing the argument list is acting a bit like a "variable declaration" - like you can't just say:

x = 10;

You need to declare that variable first:

int x = 10;

It's just here it's a variable declaration for a Type .

Now usage , that's another thing altogether - the compiler will do it damnedest to try to infer what "values" to stick in each one of those Type variables, based on usage and existing signatures, but those signatures still have to be declared.

You are mixing definition of the Where<T> method and usage of it.

When defining (and declaring) the function, you need the type parameter there to show the compiler that the method is generic. When you've declared the type parameter T , you can use it to show what types the input and output arguments have (in this case the IEnumerable<T> ).

When using the method, you don't need to specity it - it can very well be inferred from the first input argument. Try the following if you're unsure:

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var odds = numbers.Where(x => x % 2 != 0);

// test for example odds is IEnumerable<int> if you want confirmation =)
public static class Foo
{
    public static T Bar<T>(T input) { return input; }
}

Normally the type would be inferred.

Foo.Bar("test");

But what happens in this case?

Foo.Bar(null); //error

Type arguments cannot be inferred and if this was at compile time you'd get a compilation error.

Foo.Bar<string>(null); //works

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