为什么没有捕获的lambda从C#5中的静态方法更改为C#6中的实例方法
本文关键字:静态方法 实例方法 C#6 为什么 lambda C#5 | 更新日期: 2023-09-27 18:24:34
此代码在标记行上引发异常:
using System;
using System.Linq.Expressions;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Action<int, int> a = (x, y) => Console.WriteLine(x + y);
ParameterExpression p1 = Expression.Parameter(typeof(int), "p1");
ParameterExpression p2 = Expression.Parameter(typeof(int), "p2");
// Here is the exception ArgumentNullException.
MethodCallExpression call = Expression.Call(a.Method, p1, p2);
}
}
}
现在,我已经在VS2013和VS2015社区(抛出异常)中测试了这段代码。
我遵循了.Net Reference Source,这让我找到了一些代码条件,检查是否提供了方法IsStatic
。
在我的情况下,我传递的方法(a.Method
)在VS2013中是静态的,并且由于某种原因,在VS2015中是非静态的(实例)。如果没有,它抛出,告诉我我没有提供Instance
参数。
为什么会这样?如何避免这种情况,以便Expression.Call
能够在新的Visual Studio中重新开始工作?
Roslyn(VS 2015使用的C#编译器)将所有lambda方法更改为非静态方法,无论它们是否捕获变量。请参阅Roslyn中的委派缓存行为更改。正如我所解释的,这是一种允许的行为,因为不捕获变量的匿名方法(比如这里有争议的方法)的生存期要求比捕获变量的方法少。但这并不意味着这些方法必须是静态的:这只是一个实现细节。
我不知道为什么是这样(也在本地复制)。
然而,答案是:
为什么会这样?如何避免这种情况,使Expression.Call是否在新的Visual Studio中重新开始工作?
您可以做到这一点(适用于两个编译器):
Action<int, int> a = (x, y) => Console.WriteLine(x + y);
ParameterExpression p1 = Expression.Parameter(typeof(int), "p1");
ParameterExpression p2 = Expression.Parameter(typeof(int), "p2");
MethodCallExpression call;
if (a.Method.IsStatic)
{
call = Expression.Call(a.Method, p1, p2);
}
else
{
call = Expression.Call(Expression.Constant(a.Target), a.Method, p1, p2);
}
感谢Jeppe Stig Nielsen对a.Target
的修复
为什么会这样?
我不知道为什么,老实说,我没有意识到这一变化,但快速查看反编译的代码表明,对于类中所有类似的lambda,Roslyn在一个名为<>c
的单例嵌套类中生成实例方法,如
internal class Program
{
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Action<int, int> <>9__0_0;
static <>c()
{
Program.<>c.<>9 = new Program.<>c();
}
internal void <Main>b__0_0(int x, int y)
{
Console.WriteLine(x + y);
}
}
}
对我来说,这是一个突破性的变化,但我没有找到任何关于这方面的信息。
关于如何让你的代码工作,我认为@Rob的回答涵盖了这一部分。