Está permitiendo que el compilador infiera los parámetros de tipo. Esto funciona bien para el caso 1:
class SomeClass<T> where T : class {
void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression){ ... }
}
porque se declara en primer lugar una instancia:
var sc = new SomeClass<MyDateClass>();
que le dice al compilador que T is MyDateClass
. Entonces, cuando se llama al método:
sc.SomeMethod(dt => dt.Year);
el compilador sabe que T is MyDateClass
, por lo T.Year
debe ser un entero. Es suficiente información para resolver SomeMethod
como SomeMethod<MyDateClass, int>
.
Su segundo ejemplo, sin embargo, se indica explícitamente el parámetro (s) Tipo - diciendo al compilador que no inferir que:
sc.SomeMethod<MyDateClass>(dt => dt.Year);
Por desgracia, se está tratando de llamar SomeMethod
con un solo tipo parámetro (el bit <MyDateClass>
). Como eso no existe, se quejará y dirá que necesita 2 parámetros de tipo.
En cambio, si se va a llamar como lo hizo el primer ejemplo:
sc.SomeMethod(dt => dt.Year);
El compilador se quejará, que le dice que no puede inferir los parámetros de tipo - simplemente no hay suficiente información para determinar qué dt
es. Por lo tanto, se puede establecer explícitamente ambos parámetros Tipo:
sc.SomeMethod<MyDateClass, int>(dt => dt.Year);
O decirle al compilador lo dt
es (que es lo que hizo en el primer ejemplo, por Newing SomeClass<MyDateClass>
):
sc.SomeMethod((MyDateClass dt) => dt.Year);
ediciones y Actualizaciones:
¿De manera efectiva el compilador está realizando magia en el primer ejemplo? ¿Asumiendo que TProperty debería ser un int porque .Year es un int? Eso respondería a mi pregunta, no puedo decir que sea satisfactorio.
No es realmente mágico, sino una serie de pequeños pasos.Para illustate:
sc
es de tipo SomeClass<MyDateClass>
, haciendo T = MyDateClass
SomeMethod
se llama con un parámetro de dt => dt.Year
.
dt
debe ser una T (que es como se define SomeMethod
), que debe ser un MyDateClass
para la instancia sc
.
dt.Year
debe ser un int, ya que MyDateClass.Year
se declara como int.
- Desde
dt => dt.Year
se siempre devuelven un int, el regreso de expression
debe ser int. Por lo tanto, TProperty = int
.
- Eso hace que el parámetro
expression
a SomeMethod
, Expression<Func<MyDateClass,int>>
. Recuerde que T = MyDateClass
(paso 1) y TProperty = int
(paso 5).
- Ahora hemos descubierto todos los parámetros de tipo, haciendo
SomeMethod<T, TProperty>
= SomeMethod<MyDateClass, int>
.
[B] ut cuál es la diferencia entre especificar T a nivel de clase y especificando en el nivel de método? SomeClass<SomeType>
vs. SomeMethod<SomeType>
... en ambos casos, T se sabe, ¿no es así?
Sí, T
es conocido en ambos casos. El problema es que SomeMethod<SomeType>
llama a un método con un solo parámetro de tipo. En el ejemplo 2, hay parámetros de tipo. Puede hacer que el compilador infiera todos los parámetros de tipo para el método o ninguno de ellos. No puede pasar 1 y hacer que infiera el otro (TProperty
). Por lo tanto, si va a indicar explícitamente T
, también debe indicar explícitamente TProperty
.
¿De manera efectiva el compilador está realizando magia en el primer ejemplo? ¿Asumiendo que TProperty debería ser un int porque .Year es un int? Eso respondería a mi pregunta, no puedo decir que sea satisfactorio. :) –
... Creo que la luz simplemente se apagó. La diferencia entre la clase y el método es que la clase implica que una instancia de T existe en contexto mientras que el método no? De ahí las suposiciones del compilador? –
No estoy seguro de por qué no está satisfecho, pero la inferencia de tipo estático no es mágica, y tiene reglas explícitas y es una característica nueva en C#. –