Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type inference on nested generic functions

I've searched a bit about type inference, but I can't seem to apply any of the solutions to my particular problem.

I'm doing a lot of work with building and passing around functions. This seems to me like it should be able to infer the int type. The only thing I can think of is that the lambda return type isn't checked by the type inference algorithm. I have stripped unnecessary logic to show the issue more clearly.

Func<T> Test<T>(Func<Func<T>> func)
{
    return func();
}

this compiles:

Func<int> x = Test<int>(() =>
    {
        int i = 0;
        return () => i;
    });

but this gives the error "The type arguments for method cannot be inferred from the usage. Try specifying the type arguments explicitly":

Func<int> x = Test(() =>
    {
        int i = 0;
        return () => i;
    });

I guess I would just like to know why it works this way and any workarounds.

like image 598
Kelly Selden Avatar asked Mar 29 '13 02:03

Kelly Selden


1 Answers

I would say that the correct answer to the question is given by E.Lippert in SO Why can't an anonymous method be assigned to var?

But let us play with your example a little:

Func<Func<int>> f = () =>
{
    int i = 0;
    return () => i;
};

Func<int> x = Test(f); //it compiles OK

No problem in type inference with your Func<T> Test<T>(Func<Func<T>> func) here. The problem is hidden in that you use an anonymous lambda expression, the type of which cannot be inferred. Try this:

var f = () =>
{
    int i = 0;
    return () => i;
};

It gives Compiler Error CS0815, saying

Cannot assign lambda expression to an implicitly-typed local variable

and the explanation is:

An expression that is used as the initializer for an implicitly typed variable must have a type. Because anonymous function expressions, method group expressions, and the null literal expression do not have a type, they are not appropriate initializers. An implicitly typed variable cannot be initialized with a null value in its declaration, although it can later be assigned a value of null.

Now let's try another thing:

var x = Test(() =>
{
    Func<int> f = () => 0;
    return f;
});

It compiles as well. So the problem with your original example was actually with this line:

return () => i; 

We can go further and according to what Eric Lippert says in his answer provide another function to wrap this:

static Func<T> GetFunc<T>(Func<T> f) { return f; }

Now we can rewrite your code like:

var x = Test(() =>
{
    int i = 0;
    return GetFunc(() => i);
});

And it works as well.

However, as far as I understand, this all is an overhead and you should just provide an explicit type. While these workarounds are suitable, when you need to have a lambda, returning an object of anonymous type.

like image 171
horgh Avatar answered Nov 11 '22 11:11

horgh