2015-05-08 10 views
2

Я хочу подтвердить свое предположение, что LINQ Expression API не имеет средств для создания выражения, которое представляет собой создание локальной переменной.Предоставляет ли LINQ Expression API никакого способа создания переменной?

Другими словами, вы не можете создать выражение для представления:

int local; 

поскольку это объявление переменной заявление и API не поддерживает выписки лямбды. Единственное состояние, в котором выражение lambda, представленное API выражения LINQ (а не экземпляр делегата), может работать с параметрами, которые он получает, и захваченными переменными, которые он получает через закрытие.

Является ли мое предположение (основанное на нескольких месяцах практики API выражения LINQ) правильным?

+1

Вы имеете в виду, если деревья выражения .NET поддерживают объявления внутри выражения? Так, например, если вы попробуете 'System.Linq.Expressions.Expression expr =() => {int local; }; '(попытайтесь построить дерево выражений из C# lambda), вы получите ошибку _error CS0834: выражение lambda с телом оператора не может быть преобразовано в дерево выражений_. Поэтому он не поддерживается для стрелок C# лямбда, по крайней мере. –

ответ

8

False. Есть некоторые перегрузки Expression.Block, чтобы сделать это.

Верно то, что вы не можете создать выражение лямбда посредством использования компилятора C# с переменной, но это ограничение компилятора.

Таким образом, вы не можете

Expression<Func<int>> exp =() => { 
    int v = 1; 
    return v; 
}; 

но вы можете

var variable = Expression.Variable(typeof(int)); 
var lambda = Expression.Lambda<Func<int>>(
    Expression.Block(
     new[] { variable }, 
     Expression.Assign(variable, Expression.Constant(1)), 
     variable)); // With lambda expressions, there is an implicit 
        // return of the last value "loaded" on the stack 

, так что переменная оператор объявления, и API не поддерживает выписки лямбды.

Это было прав в .NET < 4.0. В .NET 4.0 Microsoft добавила Expression методы для создания почти всего, что может присутствовать в теле метода (есть некоторые недостающие «вещи», такие как небезопасные ключевые слова/операторы кода, плюс есть примитивы, но нет сложных конструкций как for или lock, которые могут быть построены поверх других конструкций). Обратите внимание, что 90% добавленных вещей несовместимы с LINQ-to-SQL/EF.

+0

спасибо. –

+0

Хотел бы я отметить свой ответ, а также правильный. Я нашел его чрезвычайно полезным. –

3

Ну, вы можете использовать Expression.Block объявить блок, который содержит локальные переменные ...

Например:

using System; 
using System.Linq.Expressions; 

public class Test 
{  
    static void Main() 
    { 
     var x = Expression.Variable(typeof(int), "x"); 
     var assignment1 = Expression.Assign(x, Expression.Constant(1, typeof(int))); 
     var assignment2 = Expression.Assign(x, Expression.Constant(2, typeof(int))); 

     var block = Expression.Block(new[] { x }, new[] { assignment1, assignment2 }); 
    } 
} 

Это строит дерево, эквивалентное выражение для:

{ 
    int x; 
    x = 1; 
    x = 2; 
} 

Компилятор C# не использует эту функцию в преобразованиях ямбда-выражения в деревья выражений, которые в настоящее время все еще ограничены выражением lambdas, поскольку насколько я знаю.

+0

Большое спасибо. Я сейчас смущен. Мне нужно исследовать еще кое-что. Причиной моей путаницы был код внутри метода RequiresCanWrite, который вызывается изнутри, например, в выражение.Назначить метод фабрики. Он гарантирует, что левая сторона задания является либо параметром, либо индексом (для массива или свойства) или выражением доступа к членству (свойство или поле). Это заставило меня попробовать проверить, могу ли я создать локальный и назначить его. Я вижу внутри кода фактор 'Expression.Variable', что он создает выражение параметра. Чтобы быть contd ... –

+0

Я хочу еще подумать, прежде чем печатать больше комментариев. Еще одна путаница в том, что C# имеет область действия блока. Итак, эта переменная, которую вы создали внутри блока, действительно является локальной переменной? У блоков есть свои собственные стековые фреймы в CLR? –

+1

@ WaterCoolerv2: Это * представление * кода - это не IL. И блоки обычно не имеют отдельных кадров стека в нормальном C# в любом случае ... –