Вы позволяете компилятору выводить параметры типа. Это прекрасно работает для случая 1:
class SomeClass<T> where T : class {
void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}
потому что вы сначала объявляете экземпляр:
var sc = new SomeClass<MyDateClass>();
, который сообщает компилятору, что T is MyDateClass
. Затем при вызове метода:
sc.SomeMethod( dt => dt.Year );
компилятор знает, что T is MyDateClass
, поэтому T.Year
должно быть int. Этого достаточно для разрешения SomeMethod
как SomeMethod<MyDateClass, int>
.
Ваш второй пример, однако, явно указывает параметры типа (типов) - говорит компилятору не выводит его:
sc.SomeMethod<MyDateClass>( dt => dt.Year );
К сожалению, он пытается вызвать SomeMethod
только с одним параметром типа (бит <MyDateClass>
). Так как этого не существует, он будет жаловаться и скажет, что вам нужно 2 параметра типа.
Если вместо этого вам нужно было назвать это так, как вы сделали в первом примере:
sc.SomeMethod( dt => dt.Year );
Компилятор будет жаловаться, говоря, что он не может определить параметры типа - просто недостаточно информации, чтобы определить, что такое dt
. Таким образом, вы можете явно указать оба параметра типа:
sc.SomeMethod<MyDateClass, int>( dt => dt.Year );
Или, скажите компилятору, что такое dt
(это то, что вы сделали в первом примере, обновив SomeClass<MyDateClass>
):
sc.SomeMethod((MyDateClass dt) => dt.Year );
Правки и обновления:
Так эффективно ли компилятор выполняет магию в первом примере? Делая предположение, что TProperty должен быть int, потому что .Year - int? Это ответило бы на мой вопрос, но не могу сказать, что это удовлетворяет.
Не совсем волшебство, но серия маленьких шагов. Иллюстрировать:
sc
имеет тип SomeClass<MyDateClass>
, что делает T = MyDateClass
SomeMethod
вызывается с параметром dt => dt.Year
.
dt
должен быть T (вот как определяется SomeMethod
), который должен быть MyDateClass
для экземпляра sc
.
dt.Year
должен быть int, поскольку MyDateClass.Year
объявлено как int.
- Поскольку
dt => dt.Year
будет всегда возвращать int, возвращаемое значение expression
должно быть int. Поэтому TProperty = int
.
- Это делает параметр от
expression
до SomeMethod
, Expression<Func<MyDateClass,int>>
. Напомним, что T = MyDateClass
(шаг 1) и TProperty = int
(шаг 5).
- Теперь мы выяснили все параметры типа, сделав
SomeMethod<T, TProperty>
= SomeMethod<MyDateClass, int>
.
[B] но в чем разница между указанием T на уровне класса и указанием на уровне метода? SomeClass<SomeType>
против SomeMethod<SomeType>
... в обоих случаях известно Т, не так ли?
Да, T
известен в обоих случаях. Проблема в том, что SomeMethod<SomeType>
вызывает метод только с одним параметром типа. В примере 2 есть параметры типа 2 . Вы можете либо указать компилятору все параметры типа для метода, либо none из них. Вы не можете просто пройти 1 и сделать так, чтобы оно выводило другое (TProperty
). Итак, если вы собираетесь явно указать T
, то вы также должны явно указать TProperty
.