Can someone explain why the first of the two following examples is valid and the other is not? More specifically, how is a relationship created between T and TProperty in the first example?
//Example 1
class SomeClass<T> where T : class
{
void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}
//Example 2
class SomeClass
{
void SomeMethod<T,TProperty>( Expression<Func<T,TProperty>> expression )
where T : class{ ... }
}
Given the two examples I would expect that the following implementations would work, but the second one does not.
//Implementation w/ Example 1
var sc = new SomeClass<MyDateClass>();
sc.SomeMethod( dt => dt.Year );
//Implementation w/ Example 2
var sc = new SomeClass();
sc.SomeMethod<MyDateClass>( dt => dt.Year );
What I’m having difficulty wrapping my mind around is how the first example/implementation can ignore the TProperty generic type when executing SomeMethod, yet the second example/implementation can’t and how an implicit relationship seems to be established between T and TProperty in example/implementation 1.
Solution
Change signature of method in example 2 as follows:
void SomeMethod<T>( Expression<Func<T,Object>> expression ){ ... }
While this will allow arbitrary objects to be used in the expression as a result it does allow for property articulation as described in implementation 2.
You’re allowing the compiler to infer the type parameters. This works fine for case 1:
because you first declare an instance:
which tells the compiler that
T is MyDateClass. Then, when you call the method:the compiler knows that
T is MyDateClass, soT.Yearmust be an int. That’s enough info to resolveSomeMethodasSomeMethod<MyDateClass, int>.Your second example, however, is explicitly stating the type parameter(s) – telling the compiler to not infer it:
Unfortunately, it is trying to call
SomeMethodwith only a single type parameter (the<MyDateClass>bit). Since that doesn’t exist, it’ll complain and say you need 2 type parameters.If, instead, you were to call it like you did the first example:
The compiler will complain, telling you that it cannot infer the type parameters – there’s simply not enough info to determine what
dtis. So, you could explicitly state both type parameters:Or, tell the compiler what
dtis (which is what you did in the first example by newingSomeClass<MyDateClass>):Edits and Updates:
Not really magic, but a series of small steps. To illustate:
scis of typeSomeClass<MyDateClass>, makingT = MyDateClassSomeMethodis called with a parameter ofdt => dt.Year.dtmust be a T (that’s howSomeMethodis defined), which must be aMyDateClassfor the instancesc.dt.Yearmust be an int, asMyDateClass.Yearis declared an int.dt => dt.Yearwill always return an int, the return ofexpressionmust be int. ThereforeTProperty = int.expressiontoSomeMethod,Expression<Func<MyDateClass,int>>. Recall thatT = MyDateClass(step 1), andTProperty = int(step 5).SomeMethod<T, TProperty>=SomeMethod<MyDateClass, int>.Yes,
Tis known in both cases. The problem is thatSomeMethod<SomeType>calls a method with only a single type parameter. In example 2, there are 2 type parameters. You can either have the compiler infer all type parameters for the method, or none of them. You can’t just pass 1 and have it infer the other (TProperty). So, if you’re going to explicitly stateT, then you must also explicitly stateTProperty.