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

Why has a lambda with no capture changed from a static in C# 5 to an instance method in C# 6?

此代码在标记行抛出异常:

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 中再次开始工作?

我不知道为什么会这样(也在本地转载)。

然而,答案是:

Why is it so? How can this be avoided so that Expression.Call would begin to work again in new 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

Why is it so?

我不知道为什么,老实说我并没有意识到这个变化,但是快速查看反编译代码表明对于 class Roslyn 中所有类似的 lambda 生成 实例 单例中的方法嵌套 class 调用 <>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 的回答涵盖了这一部分。

Roslyn(VS 2015 使用的 C# 编译器)将所有 lambda 方法更改为非静态方法,无论它们是否捕获变量。请参阅 . As I explain,这是一种允许的行为,因为不捕获变量的匿名方法(如此处讨论的那些方法)的生命周期要求比那些捕获变量的要少。但这并不意味着这些方法必须是静态的:这只是一个实现细节。