为什么没有捕获的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中重新开始工作?

为什么没有捕获的lambda从C#5中的静态方法更改为C#6中的实例方法

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的回答涵盖了这一部分。