I have a generic function that is supposed to traverse a sequence of properties within a LINQ expression to compare against another passed-in value but discovered that the LINQ expression contained a convert operation, which my function was not expecting. I've been going over the C# type inference docs to see how this could have happened but haven't been having a lot of luck figuring it out. In the process I ended up simplifying the problem to the following.
The generic function:
public static void M<T>(Func<T> func, T value)
{
}
In this block of code the type of T
is inferred to be int?
, which I wasn't expecting and was trying to figure out why.
int? value = null;
M(() => 5, value);
I was comparing it against this next block of code which fails type inference.
int? value = null;
Func<int> function = () => 5;
M(function, value);
I believe I understand why type inference fails here: the return type of the function no longer needs to be inferred and Func<int>
has no implicit conversion to Func<int?>
since they are dealing with value types, not reference types.
In the first example it seems to me that type inference sees that the anonymous method's expression type of int
is implicity convertible to int?
and then infers the anonymous method's type to be Func<int?>
.
Is this what is happening or is something else going on here?
If so, I'm kind-of surprised to see type inference reach into the expression and implicitly convert what it was returning.
You're basically thinking along the right lines, although I wouldn't phrase it quite as you did in the question. Type inference is massively complicated, and just reading through the C# standard I'm fairly convinced I've spotted at least one error (xᵢ
should be pᵢ
in some places; I'll try to get that fixed next week), so I'll avoid the formal process.
Instead, let's look at the two situations separately:
public static void M<T>(Func<T> func, T value) {}
...
int? value = null;
M(() => 5, value);
There are two arguments: an anonymous function, and a simple variable of type int?
.
The inference process will infer:
5
to T
, in order for there to be a conversion from the anonymous function to Func<T>
int?
(the type of the value
variable) to T
, in order for the second argument to be validMassively hand-wavy, but it's reasonable to imagine the compiler saying, "Hey, let's just try T=int?
here":
5
to int?
. So we can convert () => 5
to a Func<int?>
int?
to int?
public static void M<T>(Func<T> func, T value) {}
...
int? value = null;
Func<int> function = () => 5;
M(function, value);
Here, type inference requires:
Func<int>
to Func<T>
for the first argument to be valid - which means there must be an identity conversion from int
to T
int?
to T
for the second argument to be validType inference fails as there's no type T
that satisfies both of those constraints.