类属性的 Lambda 表达式

发布于 2024-07-29 21:56:27 字数 1041 浏览 0 评论 0 原文

有人可以解释为什么以下两个示例中的第一个有效而另一个无效? 更具体地说,第一个示例中 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{ ... }
}

鉴于这两个示例,我希望以下实现能够工作,但第二个实现不行。

//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 );

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

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

void SomeMethod<T>( Expression<Func<T,Object>> expression ){ ... }

虽然这将允许在表达式中使用任意对象作为结果,但它确实允许如实现 2 中所述的属性表达。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

儭儭莪哋寶赑 2024-08-05 21:56:28

首先,您的第一个示例不正确,并且不会按给定方式编译。 除了方法上缺少 public 之外,您还定义了两次 T - 一次在类上,一次在方法上。 这本身并不是一个错误(尽管您会收到警告),但是在编译方法调用时,您会收到与示例 #2 中描述的相同的错误。 因此,我假设实际的代码是这样的:

//Example 1  
class SomeClass<T> where T : class
{
  public void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression) {}
}

此外,您的两个示例都“忽略”(即推断)对 SomeMethod 的调用中 TProperty 的实际类型。 您的第二个示例明确指定了 T 的类型,是的,但不是 TProperty

第一个示例中也没有建立隐式关系。 当您使用 T=MyDateClass 实例化 SomeClass<> 时,该实例化的 SomeMethod 签名实际上变为:

void SomeMethod<TProperty>(Expression<Func<MyDateClass, TProperty>> expression)

因此 lambda 的参数类型此时已知,因此您不必指定它。 表达式 lambda 的返回类型被推断为 => 右侧表达式的类型,这将是 TProperty 的推断类型。

在第二个示例中,由于在实例化类时未显式指定 T,并且无法从方法参数推断它,因此必须显式指定它。 指定后,TProperty 将以与示例 #1 完全相同的方式推断。

First of all, your first example is incorrect, and won't compile as given. Aside from the missing public on the method, you define T twice - once on class, and once on method. This isn't an error in and of itself (though you'll get a warning), but when compiling the method call, you'll get the same error as you describe getting for example #2. So, I assume the actual code is like this:

//Example 1  
class SomeClass<T> where T : class
{
  public void SomeMethod<TProperty>(Expression<Func<T,TProperty>> expression) {}
}

Furthermore, both your examples "ignore" (that is, infer) the actual type of TProperty in your call to SomeMethod. Your second example explicitly specifies the type of T, yes, but not TProperty.

There's no implicit relationship established in the first example, either. When you instantiate SomeClass<> with T=MyDateClass, the signature of SomeMethod for that instantiation effectively becomes:

void SomeMethod<TProperty>(Expression<Func<MyDateClass, TProperty>> expression)

So the argument type of the lambda is known at this point, so you don't have to specify it. The return type of expression lambda is inferred as the type of expression on the right of =>, and that will be the inferred type of TProperty.

In the second example, since T was not explicitly specified when instantiating the class, and since there's no way to infer it from method arguments, it has to be specified explicitly. Once you specify it, TProperty is inferred in exact same way as for example #1.

土豪我们做朋友吧 2024-08-05 21:56:28

您允许编译器推断类型参数。 这适用于情况 1:

class SomeClass<T> where T : class {
    void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}

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

var sc = new SomeClass<MyDateClass>();

它告诉编译器 T 是 MyDateClass。 然后,当您调用该方法时:

sc.SomeMethod( dt => dt.Year );

编译器知道T是MyDateClass,因此T.Year必须是int。 这些信息足以将 SomeMethod 解析为 SomeMethod

然而,您的第二个示例明确声明了类型参数 - 告诉编译器推断它:

sc.SomeMethod<MyDateClass>( dt => dt.Year );

不幸的是,它试图仅使用一个来调用 SomeMethod类型参数( 位)。 由于它不存在,它会抱怨并说你需要 2 个类型参数。

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

sc.SomeMethod( dt => dt.Year );

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

sc.SomeMethod<MyDateClass, int>( dt => dt.Year );

或者,告诉编译器dt是什么(这就是您在第一个示例中通过新建SomeClass所做的事情) ;):

sc.SomeMethod((MyDateClass dt) => dt.Year );

编辑和更新:

编译器在第一个示例中执行魔法的效果如此之好? 假设 TProperty 应该是 int 因为 .Year 是 int? 这可以回答我的问题,但不能说它令人满意。

并不是真正的魔法,而是一系列的小步骤。 说明一下:

  1. sc 的类型为 SomeClass,使得 T = MyDateClass
  2. 使用参数调用 SomeMethod dt => 的 dt.年份
  3. dt 必须是一个T(这就是SomeMethod的定义方式),它必须是一个MyDateClass< /code> 为实例 sc
  4. dt.Year 必须是一个 int,因为 MyDateClass.Year 被声明为一个 int。
  5. 由于dt => dt.Year总是返回一个int,表达式的返回必须是int。 因此TProperty = int
  6. 这使得参数表达式变为SomeMethodExpression>。 回想一下 T = MyDateClass(步骤 1)和 TProperty = int(步骤 5)。
  7. 现在我们已经计算出所有类型参数,使得 SomeMethod = SomeMethod

[B]但是在类级别指定 T 和在方法级别指定 T 之间有什么区别? SomeClassSomeMethod...在这两种情况下 T 都是已知的,不是吗?

是的,T 在这两种情况下都是已知的。 问题在于 SomeMethod 调用仅具有单个类型参数的方法。 在示例 2 中,有 2 个类型参数。 您可以让编译器推断该方法的所有 类型参数,也可以不推断 任何类型参数。 您不能只传递 1 并让它推断另一个 (TProperty)。 因此,如果您要显式声明 T,那么您还必须显式声明 TProperty

You're allowing the compiler to infer the type parameters. This works fine for case 1:

class SomeClass<T> where T : class {
    void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}

because you first declare an instance:

var sc = new SomeClass<MyDateClass>();

which tells the compiler that T is MyDateClass. Then, when you call the method:

sc.SomeMethod( dt => dt.Year );

the compiler knows that T is MyDateClass, so T.Year must be an int. That's enough info to resolve SomeMethod as SomeMethod<MyDateClass, int>.

Your second example, however, is explicitly stating the type parameter(s) - telling the compiler to not infer it:

sc.SomeMethod<MyDateClass>( dt => dt.Year );

Unfortunately, it is trying to call SomeMethod with 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:

sc.SomeMethod( dt => dt.Year );

The compiler will complain, telling you that it cannot infer the type parameters - there's simply not enough info to determine what dt is. So, you could explicitly state both type parameters:

sc.SomeMethod<MyDateClass, int>( dt => dt.Year );

Or, tell the compiler what dt is (which is what you did in the first example by newing SomeClass<MyDateClass>):

sc.SomeMethod((MyDateClass dt) => dt.Year );

Edits and Updates:

So effectively the compiler is performing magic in the first example? Making an assumption that TProperty should be an int because .Year is an int? That would answer my question, can't say it's satisfying though.

Not really magic, but a series of small steps. To illustate:

  1. sc is of type SomeClass<MyDateClass>, making T = MyDateClass
  2. SomeMethod is called with a parameter of dt => dt.Year.
  3. dt must be a T (that's how SomeMethod is defined), which must be a MyDateClass for the instance sc.
  4. dt.Year must be an int, as MyDateClass.Year is declared an int.
  5. Since dt => dt.Year will always return an int, the return of expression must be int. Therefore TProperty = int.
  6. That makes the parameter expression to SomeMethod, Expression<Func<MyDateClass,int>>. Recall that T = MyDateClass (step 1), and TProperty = int (step 5).
  7. Now we've figured out all of the type parameters, making SomeMethod<T, TProperty> = SomeMethod<MyDateClass, int>.

[B]ut what's the difference between specifying T at the class level and specifying at the method level? SomeClass<SomeType> vs. SomeMethod<SomeType>... in both cases T is known is it not?

Yes, T is known in both cases. The problem is that SomeMethod<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 state T, then you must also explicitly state TProperty.

花开半夏魅人心 2024-08-05 21:56:28

据我所知,问题很简单, DateTime 不是一个类......您将 T 作为 DateTime 传递>(无论是隐式的还是显式的)。

可能还有些令人困惑的是,在第一个示例中,您有两个名为 T 的类型参数 - 一个位于类型上,另一个位于方法上。 它们实际上是完全独立的......为了相同,将第一个示例重写为:

//Example 1  
class SomeClass<T> where T : class
{
    void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}

这现在是相同的 T : class

The problem, as far as I can see, is simply that DateTime isn't a class... you're passing in T as DateTime (whether it is implicit or explicit).

It is probably also slightly confusing that in the first example you have two type parameters called T - one on the type, and one on the method. They are actually completely separate... to be the same, rewrite the first example as:

//Example 1  
class SomeClass<T> where T : class
{
    void SomeMethod<TProperty>( Expression<Func<T,TProperty>> expression ){ ... }
}

This is now the same T : class

掩饰不了的爱 2024-08-05 21:56:28

不要输入具体细节,请查看 埃里克·利珀特。 我想它会回答你想问的问题。

我知道它又长又做作,但我认为这是回答你的最佳选择。

Rather than typing out the specifics, please take a look at this post by Eric Lippert. I think it will answer what you're attempting to ask.

I realize it's long and contrived, but I think it's the best bang for the buck for answering you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文