I am not exactly sure how to make this question readable/understandable, but hear my out and I hope you will understand my issue when we get to the end (at the very least, it is easily reproduceable).
I try to call a method used for validating results in UnitTests. It has the following signature:
void AssertPropertyValues<TEnumerable, TElement, TProperty>(
TEnumerable enumerable,
Func<TElement, TProperty> propertyPointer,
params TProperty[] expectedValues)
where TEnumerable : System.Collections.Generic.IList<TElement>
What this means is, that it takes the following input
So, an actual execution of this method could look like this:
AssertPropertyValues(
item.ItemGroups,
itemGroup => itemGroup.Name,
"Name1", "Name2", "Name3");
At least, that is how I would like it to look like, but I run into the well known compiler error: "The type arguments for the method 'X' cannot be inferred from the usage.", and that is what I do not understand. It should have all the info needed as far as I can see, or perhaps it is another version of the "Covariance and Contravariance" problem?
So for now I am forced to do it like this instead:
AssertPropertyValues(
item.ItemGroups,
(ItemGroup itemGroup) => itemGroup.Name,
"Name1", "Name2", "Name3");
Can anyone point out why this scenario can not be inferred by the compiler?
Your problem is caused by the fact that constraints are not considered part of the signature and are never used to make deductions during type inference. You are expecting the inference to go:
TEnumerable
is determined by taking the type of the first argument.TElement
is determined by taking the IList<T>
implementation information from TElement
TProperty
is determined by the type of the body of the lambdaBut C# never makes that second step because that requires considering information from a constraint. As you note, if you provide that information in the lambda then the compiler makes the deduction based on the formal parameter type.
Fortunately your constraint is completely unnecessary. Rewrite your method to have a simpler signature that doesn't have a constraint:
void AssertPropertyValues<TElement, TProperty>(
IList<TElement> sequence,
Func<TElement, TProperty> projection,
params TProperty[] expectedValues)
and now you should be fine.
And while you're at it, you should probably simplify that to IEnumerable<TElement>
unless you need an IList<T>
for some reason.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With