Почему лямбда без захвата изменилась с статического в С# 5 на метод экземпляра в С# 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 Community (генерирует исключение).
Я следил за .Net Reference Source, который привел меня к некоторому кодовому условию, которое проверяет, предоставлен ли метод IsStatic
или нет.
В моем случае метод я pass (a.Method
) является статическим в VS2013 и по какой-то причине нестационарный (экземпляр) в VS2015. Если нет, то он подсказывает, что я не представил аргумент Instance
.
Почему так? Как этого можно избежать, чтобы Expression.Call
снова начал работать в новой Visual Studio?
Ответы
Ответ 1
У меня нет ответа, почему это так (воспроизводится локально тоже).
Однако ответ на вопрос:
Почему так? Как этого можно избежать, чтобы 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
Ответ 2
Roslyn (компилятор С#, используемый VS 2015) изменил все методы лямбда на нестатические методы, независимо от того, захватывают ли они переменные или нет. См. Делегировать изменения поведения кэширования в Roslyn. Поскольку я объясняю, это допустимое поведение, потому что анонимные методы (например, те, которые возникают здесь), которые не фиксируют переменные, имеют меньше требований к жизненному циклу, чем те, которые делают. Это не означает, однако, что эти методы должны быть статическими: это всего лишь деталь реализации.
Ответ 3
Почему это так?
Я не знаю, почему, и честно не знал об этом изменении, но, быстро взглянув на декомпилированный код, показал, что для всех подобных лямбда внутри класса Roslyn генерирует методы instance в одноэлементном вложенный класс под названием <>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 охватывает эту часть.