类属性的Lambda表达式

Ika*_*ior 7 .net c# generics lambda

有人可以解释为什么以下两个例子中的第一个是有效的而另一个不是吗?更具体地说,在第一个例子中,如何在T和TProperty之间建立关系?

//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{ ... }
}
Run Code Online (Sandbox Code Playgroud)

鉴于这两个例子,我希望以下实现可以工作,但第二个不会.

//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 );
Run Code Online (Sandbox Code Playgroud)

我无法理解的是第一个示例/实现在执行SomeMethod时如何忽略TProperty泛型类型,但第二个示例/实现不能以及如何在T和TProperty之间建立隐式关系示例/实施1.

解决方案 更改示例2中方法的签名如下:

void SomeMethod<T>( Expression<Func<T,Object>> expression ){ ... }
Run Code Online (Sandbox Code Playgroud)

虽然这将允许在表达式中使用任意对象,因此它确实允许属性清晰度,如实现2中所述.

Mar*_*ett 7

您允许编译器推断类型参数.这适用于案例1:

class SomeClass<T> where T : class {
    void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}
Run Code Online (Sandbox Code Playgroud)

因为你首先声明一个实例:

var sc = new SomeClass<MyDateClass>();
Run Code Online (Sandbox Code Playgroud)

它告诉编译器T is MyDateClass.然后,当你调用方法时:

sc.SomeMethod( dt => dt.Year );
Run Code Online (Sandbox Code Playgroud)

编译器知道T is MyDateClass,所以T.Year必须是一个int.这是足够的信息来解决SomeMethodSomeMethod<MyDateClass, int>.

但是,您的第二个示例是明确声明类型参数 - 告诉编译器不要推断它:

sc.SomeMethod<MyDateClass>( dt => dt.Year );
Run Code Online (Sandbox Code Playgroud)

不幸的是,它试图SomeMethod仅使用单个类型参数(<MyDateClass>位)进行调用.由于这不存在,它会抱怨并说你需要2个类型的参数.

相反,如果您像第一个例子那样调用它:

sc.SomeMethod( dt => dt.Year );
Run Code Online (Sandbox Code Playgroud)

编译器会抱怨,告诉你它不能推断类型参数 - 根本没有足够的信息来确定是什么dt.因此,您可以显式地声明两个类型参数:

sc.SomeMethod<MyDateClass, int>( dt => dt.Year );
Run Code Online (Sandbox Code Playgroud)

或者,告诉编译器是什么dt(通过newing在第一个示例中执行的操作SomeClass<MyDateClass>):

sc.SomeMethod((MyDateClass dt) => dt.Year );
Run Code Online (Sandbox Code Playgroud)

编辑和更新:

在第一个例子中,编译器是否有效地执行魔术?假设TProperty应该是一个int因为.Year是一个int?那会回答我的问题,但不能说它令人满意.

不是很神奇,而是一系列小步骤.说明:

  1. sc是类型SomeClass<MyDateClass>,制作T = MyDateClass
  2. SomeMethod用参数调用dt => dt.Year.
  3. dt 必须是一个T(这是如何SomeMethod定义的),它必须是一个MyDateClass实例sc.
  4. dt.Year 必须是一个int,因为MyDateClass.Year声明为int.
  5. 由于dt => dt.Year始终返回一个int,所以返回expression 必须为int.因此TProperty = int.
  6. 这使得参数expressionSomeMethod,Expression<Func<MyDateClass,int>>.回想一下T = MyDateClass(步骤1)和TProperty = int(步骤5).
  7. 现在我们已经找出了所有的类型参数,使得SomeMethod<T, TProperty>= SomeMethod<MyDateClass, int>.

[B]在类级别指定T和在方法级别指定之间有什么区别?SomeClass<SomeType>SomeMethod<SomeType>...在两种情况下T都知道不是吗?

是的,T在两种情况下都是已知的.问题是SomeMethod<SomeType>调用只有一个类型参数的方法.在示例2中,有2个类型参数.您可以让编译器推断该方法的所有类型参数,或者推断它们.你不能只传递1并让它推断另一个(TProperty).因此,如果您要明确说明T,那么您还必须明确说明TProperty.