Wednesday, August 6, 2014

Deep understanding expression. Part 2

Продолжим наше увлекательное погружение в мир деревьев выражений языка C#. Мне очень понравилось работать с предыдущей статьей, так как я сам узнал много нового о деревьях выражений и их использовании. Поэтому надеюсь, что сегодняшний материал будет для вас не менее занятным. Предыдущая статья закончилась рассмотрением функции Equal, пожалуй, продолжу в том же духе рассматривать в алфавитном порядке далее.
ExclusiveOr
Метод Expression.ExclusiveOr создает BinaryExpression, который представляет собой побитовую операцию XOR (побитовая операция исключающего OR). 
Так будет выглядеть таблица соответствия:
Пример, как это будет выглядеть, если расписать в виде таблицы:
Пример:
Expression exclusiveOrExpr = Expression.ExclusiveOr(
    Expression.Constant(5), 
    Expression.Constant(3)
    );
            
Console.WriteLine(Expression.Lambda<Func<int>>(exclusiveOrExpr).Compile()());
Результат:
6
ExclusiveOrAssign
Этот метод аналогичен предыдущему рассмотренному методу, за исключением того, что в данном методе мы присваиваем полученный результат в переменную. Поэтому не отвлекаясь на детальное расписывание, просто посмотрим пример, так как мы уже рассматривали варианты использования методов для построения деревьев выражений с приставкой Assign в конце метода.
Пример:
ParameterExpression parameterExpr = Expression.Parameter(typeof (int), "n");
Expression assignExpr = Expression.Assign(parameterExpr, Expression.Constant(5));
Expression bodyExpression = Expression.Block(
    new ParameterExpression[] { parameterExpr },
    assignExpr,
    Expression.ExclusiveOrAssign(parameterExpr, Expression.Constant(3))
    );

Console.WriteLine(Expression.Lambda<Func<int>>(bodyExpression).Compile()());
Результат:
6
Field
На этой функции хотелось бы заострить ваше внимание. Эта функция создает MemberExpression, которое позволяет получить доступ к полу по конкретному имени. Если вы помните, то чтобы, например, получить доступ к private полу в каком-то классе, нам нужно было писать очень некрасивый и не совсем интуитивно понятный код. Давайте рассмотрим пример, в котором вычитаем с тестового класса private поле.
public class A
{
    public A(string name)
    {
        _p = name;
    }

    private string _p;
    public string P
    {
        get { return _p; }
    }
}
Функция для получения поля _p приведена ниже.
static void Main(string[] args)
{
    A a = new A("Hello");
    string p = (string)(typeof(A).GetField("_p"BindingFlags.NonPublic | BindingFlags.Instance).GetValue(a));

    Console.WriteLine(p);
            
    Console.ReadLine();
}
Так как GetValue возвращает object, я сразу привел его к нужному типу. На экран после запуска будет выведен текст "Hello". 
Давайте теперь рассмотрим пример, как это можно сделать с помощью деревьев выражений с использованием функции Field.
static void Main(string[] args)
{
    A a = new A("Hello");
    ParameterExpression paramExpr = Expression.Parameter(typeof(A), "arg");
    MemberExpression member = Expression.Field(paramExpr, "_p");

    string result = Expression.Lambda<Func<Astring>>(member, paramExpr).Compile()(a);
    Console.WriteLine(result);
            
    Console.ReadLine();
}
Код выглядит симпатичнее, хотя немного больше по размеру. Но мы можем переписать наш пример по такому принципу. Выглядеть наш код после изменений будет так:
static void Main(string[] args)
{
    A a = new A("Hello");
    var getMyProperty = GetFieldAccessor<A,string> ("_p"); 

    //typed access via delegate 
    string result = getMyProperty(a);
    Console.WriteLine(result);
            
    Console.ReadLine();
}

public static Func<T, R> GetFieldAccessor<T, R>(string fieldName)
{
    ParameterExpression param =
    Expression.Parameter(typeof(T), "arg");

    MemberExpression member =
    Expression.Field(param, fieldName);

    Func<T, R> compiled = Expression.Lambda<Func<T, R>>(member, param).Compile();

    return compiled;
}
Результат:
Hello
Довольно элегантный способ получения private поля через делегат, не правда ли?
К сожалению, магии не бывает, и от рефлексии никуда не деться в данном случае, просто всю логику вместо нас берет .NET Framework. Для того чтобы убедиться в том, что мистики никакой нет, посмотрим на то, как функция Field реализована в самом фреймворке.
/// <summary>
/// Creates a <see cref="MemberExpression"/> accessing a field.
/// </summary>
/// <param name="expression">The containing object of the field.  This can be null for static fields.</param>
/// <param name="fieldName">The field to be accessed.</param>
/// <returns>The created <see cref="MemberExpression"/>.</returns>
public static MemberExpression Field(Expression expression, string fieldName)
{
    RequiresCanRead(expression, "expression");

    // bind to public names first
    FieldInfo fi = expression.Type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
    if (fi == null)
    {
        fi = expression.Type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
    }
    if (fi == null)
    {
        throw Error.InstanceFieldNotDefinedForType(fieldName, expression.Type);
    }
    return Expression.Field(expression, fi);
}
Как видим, из данного кода мы пытаемся получить, по сути, дважды FieldInfo с нашего типа данных. Мне очень нравится тот подход, который практикует компания Microsoft с выкладыванием доступа к исходникам в онлайн-режиме. Посмотреть реализацию той или иной функции можно очень детально. Это позволит более глубоко понять предметную область.
GetActionType/GetDelegateType/GetFuncType
Метод GetDelegateType возвращает объект Expression.Type для получения типа выражения и представляет generic Func и Action делегаты с аргументами конкретного типа. Если обобщить, то это способ создать динамически делегат, в котором GetDelegateType – это общая реализация, GetActionType – реализация для делегата типа Action, а GetFuncType – для типа Func.  Функция – это оболочка над функцией MakeDelegateType, которая реализована не очень сложно, и достаточно легко можно понять реализации, если посмотреть в исходный код (DelegateHelpers.Generated.cs).
internal static Type MakeDelegateType(Type[] types)
        {
            lock (_DelegateCache)
            {
                TypeInfo curTypeInfo = _DelegateCache;

                // arguments & return type
                for (int i = 0; i < types.Length; i++)
                {
                    curTypeInfo = NextTypeInfo(types[i], curTypeInfo);
                }

                // see if we have the delegate already
                if (curTypeInfo.DelegateType == null)
                {
                    // clone because MakeCustomDelegate can hold onto the array.
                    curTypeInfo.DelegateType = MakeNewDelegate((Type[])types.Clone());
                }

                return curTypeInfo.DelegateType;
            }
        }
Давайте теперь рассмотрим, как это можно использовать. Для начала реализуем класс A, в который добавим один метод Method1, с одним аргументом типа int.
public class A
{
    public void Method1(int number)
    {
        Console.WriteLine(number);
    }
}
Теперь попробуем получить наш метод Method1 с помощью функции GetDelegateType.
static void Main(string[] args)
{
    A a = new A();

    var method = typeof(A).GetMethod("Method1");
    dynamic delegateType = GetDelegate(a, method);
    delegateType(4);

    Console.ReadLine();
}

private static Delegate GetDelegate(object target, MethodInfo method)
{
    var tArgs = new List<Type>();
    foreach (var param in method.GetParameters())
        tArgs.Add(param.ParameterType);
    tArgs.Add(method.ReturnType);
    var delDecltype = Expression.GetDelegateType(tArgs.ToArray());
    return Delegate.CreateDelegate(delDecltype, target, method);
}
Результат:
4
Метод GetActionType отличается от GetFunсType тем, что GetActionType возвращает тип void. Эти методы Microsoft реализовала решением "в лоб" с помощью скрипта, написанного на питоне, если верить комментариям.
internal static Type GetActionType(Type[] types)
{
    switch (types.Length)
    {
        case 0: return typeof(Action);
        #region Generated Delegate Action Types

        // *** BEGIN GENERATED CODE ***
        // generated by function: gen_delegate_action from: generate_dynsites.py

        case 1: return typeof(Action<>).MakeGenericType(types);
        case 2: return typeof(Action<,>).MakeGenericType(types);
        case 3: return typeof(Action<,,>).MakeGenericType(types);
        case 4: return typeof(Action<,,,>).MakeGenericType(types);
        case 5: return typeof(Action<,,,,>).MakeGenericType(types);
        case 6: return typeof(Action<,,,,,>).MakeGenericType(types);
        case 7: return typeof(Action<,,,,,,>).MakeGenericType(types);
        case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types);
        case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types);
        case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types);
        case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types);
        case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types);
        case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types);
        case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types);
        case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types);
        case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types);

        // *** END GENERATED CODE ***

        #endregion

        defaultreturn null;
    }
}
Реализация Func<> подобная этой, и вы можете посмотреть ее здесь: GetFuncType. Для того чтобы это использовать, давайте немного допилим наш метод GetDelegate с примера выше.
static void Main(string[] args)
{
    A a = new A();
    dynamic delegateType = GetDelegate<A>(a, "Method1");
    delegateType(4);

    Console.ReadLine();
}

private static Delegate GetDelegate<T>(object target, string methodName)
{
    MethodInfo method = typeof(T).GetMethod(methodName);
    List<Type> args = new List<Type>(
        method.GetParameters().Select(p => p.ParameterType));
    Type delegateType;
    if (method.ReturnType == typeof(void))
    {
        delegateType = Expression.GetActionType(args.ToArray());
    }
    else
    {
        args.Add(method.ReturnType);
        delegateType = Expression.GetFuncType(args.ToArray());
    }
    return Delegate.CreateDelegate(delegateType, target, method);
}
Результат:
4
Goto
Метод Expression.Goto создает выражение GotoExpression и представляет собой реализацию перехода с помощью goto.
Пример:
static void Main(string[] args)
{
    LabelTarget label = Expression.Label();
    Expression blockExpr = Expression.Block(
        Expression.Call(
            null,
            typeof (Console).GetMethod("Write", new Type[] {typeof (String)}),
            Expression.Constant("Hello ")
            ),
        Expression.Goto(label),
        Expression.Call(
            null,
            typeof (Console).GetMethod("Write", new Type[] {typeof (String)}),
            Expression.Constant("World ")
            ),
        Expression.Label(label)
        );

    Console.WriteLine(Expression.Lambda<Action>(blockExpr).Compile().DynamicInvoke());
    Console.ReadLine();
}
Результат:
Hello
Слово "World" не будет выведено, поскольку сразу же после первого вызова Call у нас срабатывает оператор перехода goto для перехода в конек блока дерева выражения.
GreaterThen/GreaterThenOrEqual
Метод Expression.GreaterThan создает BinaryExpression для сравнения чисел на больше (a > b), а метод Expression.GreaterThanOrEqual позволяет сравнить, больше или равно число a по отношению к числу b.
Пример:
static void Main(string[] args)
{
    Expression greaterThenExpr = Expression.GreaterThan(
        Expression.Constant(5),
        Expression.Constant(4));
    Expression greaterThenOrEqualExpr = Expression.GreaterThanOrEqual(
        Expression.Constant(4),
        Expression.Constant(4));
    Console.WriteLine("GreateThen result {0}", Expression.Lambda<Func<bool>>(greaterThenExpr).Compile()());
    Console.WriteLine("GreateThenOrEqual result {0}", Expression.Lambda<Func<bool>>(greaterThenOrEqualExpr).Compile()());
    Console.ReadLine();
}
Результат:
GreateThen result True
GreateThenOrEqual result True
Небольшое пояснение. В первом случае у нас проходит сравнение 5>4, а во втором 4>=4. Вроде, пока что нет ничего сложного.
IfThen/IfThenElse
Данные методы также позволяют проверять условия с помощью оператора ifIfThen, и с помощью операторов if и elseIfThenElse.
Пример для IfThen:
static void Main(string[] args)
{
    ParameterExpression value = Expression.Parameter(typeof(int), "value");
    Expression ifThenExpr = Expression.IfThen(
        Expression.GreaterThan(value, Expression.Constant(4)),
        Expression.Call(
            null,
            typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }),
            Expression.Constant("The condition is true.")
            )
        );
    Expression.Lambda<Action<int>>(ifThenExpr, new[] { value }).Compile()(5);
           
    Console.ReadLine();
}
Результат:
The condition is true.
В данном примере мы передаем значение 5 как параметр и проверяем условие 5>4. Приведенный выше пример можно переписать следующим образом на языке C#:
int value = 5;
if (value > 4)
{
    Console.WriteLine("The condition is true.");
}
Давайте сразу же рассмотрим пример с IfThenElse. Для этого приведенный выше пример мы расширим, добавив еще один аргумент для блока else, а также сделаем так, чтобы вывелся результат с блока else.
static void Main(string[] args)
{
    ParameterExpression value = Expression.Parameter(typeof(int), "value");
    Expression ifThenExpr = Expression.IfThenElse(
        Expression.GreaterThan(value, Expression.Constant(4)),
        Expression.Call(
            null,
            typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }),
            Expression.Constant("The condition is true.")
            ),
        Expression.Call(
            null,
            typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }),
            Expression.Constant("The condition is false.")
            )
        );
    Expression.Lambda<Action<int>>(ifThenExpr, new[] { value }).Compile()(3);
           
    Console.ReadLine();
}
Результат:
The condition is false.
Пример выше на языке C# можно переписать следующим образом:
int value = 3;
if(value > 4)
    Console.WriteLine("The condition is true.");
else
{
    Console.WriteLine("The condition is false.");
}
Как говорится, все предельно просто.
Invoke
Метод Expression.Invoke создает InvocationExpression для вызова метода Invoke для делегатов или лямбда-выражений с передачей нужных аргументов. Если вы помните, как работают делегаты, то вы также, наверное, сможете вспомнить, как там используется метод Invoke. Прежде чем начать разбираться с деревьями выражений, рассмотрим простой пример на C# с использованием метода Invoke для делегатов.
Func<int, int, int> sumFunc = (a, b) => a + b;
Console.WriteLine(sumFunc(3, 4)); //implicit call invoke method
Console.WriteLine(sumFunc.Invoke(3, 4)); //explicit call invoke method
Как видим из примера, у нас есть простой делегат, который суммирует два числа. В первом случае мы не вызываем явно метод Invoke, что видно по коду, а во втором выводе на консоль вызываем. Теперь давайте этот же пример перепишем на деревьях выражений.
Expression<Func<int, int, int>> sumFuncExpr = (a, b) => a + b;

Expression invocationExpression =
    Expression.Invoke(
        sumFuncExpr,
        Expression.Constant(3),
        Expression.Constant(4));

Console.WriteLine(Expression.Lambda(invocationExpression).Compile().DynamicInvoke());
Результат:
7
IsTrue/IsFalse
Возвращает результат, который соответствует истине (IsTrue) или ложности (IsFalse).
Пример:
static void Main(string[] args)
{
    Expression isTrueExpr =
        Expression.IsTrue(
            Expression.GreaterThan(Expression.Constant(5), Expression.Constant(4)));

    Console.WriteLine(Expression.Lambda<Func<bool>>(isTrueExpr).Compile()());

    Expression isFalseExpr =
        Expression.IsFalse(
            Expression.GreaterThan(Expression.Constant(5), Expression.Constant(6)));

    Console.WriteLine(Expression.Lambda<Func<bool>>(isFalseExpr).Compile()());
           
    Console.ReadLine();
}
Результат:
True
True
Label
Создает LabelTarget, которое создает метку, используемую для перехода с помощью метода goto. Ниже представлен простой пример на языке C# с использованием метки перехода совместно с оператором goto.
static void Main(string[] args)
{
    int a = 5;
    if (a > 4)
    {
        Console.WriteLine("Before goto");
        goto b;
        Console.WriteLine("After goto");
    }
    b:
        Console.WriteLine("Label b");
    Console.ReadLine();
}
На экране будет выведен результат:
Before goto
Label b
Переписать этот код через деревья выражений можно так:
static void Main(string[] args)
{
    LabelTarget returnTarget = Expression.Label("b");

    BlockExpression blockExpr =
        Expression.Block(
            Expression.IfThen(
            Expression.GreaterThan(Expression.Constant(5), Expression.Constant(4)),
            Expression.Block(
                Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("Before goto")),
                Expression.Goto(returnTarget),
                Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("After goto")))
            ),
                   
            Expression.Label(returnTarget),
            Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("Label b"))
        );

    Expression.Lambda<Action>(blockExpr).Compile()();
    Console.ReadLine();
}
Этот код аналогичный коду, написанному перед этим, на простом C# синтаксисе без использования возможностей expressions. А теперь вы увидели все то же самое, только через деревья выражений.
Lambda
Expression.Lambda создает дерево выражений, которое представляет собой лямбда-выражение. До этого в каждом примере мы уже использовали лямбда-выражения для вывода значения на экран. Сейчас просто напишем простой пример, чтобы долго на этом не останавливаться.
Пример:
static void Main(string[] args)
{
    ParameterExpression param = Expression.Parameter(typeof(int), "arg");

    // Creating an expression for the method call and specifying its parameter.
    MethodCallExpression methodCall = Expression.Call(
        typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }),
        param
        );

    // Compiling and invoking the methodCall expression.
    Expression.Lambda<Action<int>>(
        methodCall,
        new ParameterExpression[] { param }
    ).Compile()(10);
    Console.ReadLine();
}
Результат:
10
В данном примере мы с помощью метода Console.WriteLine выводим на консоль переданное значение.
LeftShift/LeftShiftAssign
Метод Expression.LeftShift создает BinaryExpression, представляющий побитовую операцию со сдвигом влево. Метод LeftShiftAssign аналогичен методу LeftShift, за исключением того, что мы можем сохранить результат побитового сдвига в переменную.
Пример в таблице, как это работает:

Мы сдвинули значение 14 на два бита влево и получили значение 56, что и показано в таблице выше.
Если вы обратили внимание, то операции сдвига влево равносильно умножению на 2 для каждого шага. Теперь можно перейти к самому примеру, так как мы разобрали, что такое побитовый сдвиг влево.
Пример:
static void Main(string[] args)
{
    Expression leftShiftExpt = Expression.LeftShift(Expression.Constant(14), Expression.Constant(2));
    Console.WriteLine(Expression.Lambda<Func<int>>(leftShiftExpt).Compile()());
    Console.ReadLine();
}
Результат:
56
LessThan/LessThanOrEqual
Метод LessThan позволяет проводить сравнение чисел на значение "меньше чем", а метод LessThanOrEqual – на "меньше или равно" определенного значения.
Давайте возьмем пример, который мы приводили для функций GreaterThan и GreaterThanOrEqual, и просто заменим на функции, которые изучаем сейчас.
Пример:
static void Main(string[] args)
{
    Expression lessThanExpr = Expression.LessThan(
        Expression.Constant(5),
        Expression.Constant(4));
    Expression lessThanOrEqualExpr = Expression.LessThanOrEqual(
        Expression.Constant(4),
        Expression.Constant(4));
    Console.WriteLine("LessThan result {0}", Expression.Lambda<Func<bool>>(lessThanExpr).Compile()());
    Console.WriteLine("LessThanOrEqual result {0}", Expression.Lambda<Func<bool>>(lessThanOrEqualExpr).Compile()());

    Console.ReadLine();
}
Результат:
LessThan result False
LessThanOrEqual result True
ListInit
Метод Expression.ListInit – для того чтобы заполнить коллекцию с помощью Expression.ElementInit. Пример, как это выглядит в языке C#, без использования деревьев выражений.
var list = new List<string>();
list.Add("Result1");
list.Add("Result2");
Теперь то же самое сделаем с помощью деревьев выражений.
Пример:
static void Main(string[] args)
{
    MethodInfo addMethod = typeof(List<string>).GetMethod("Add");

    ElementInit elementInit1 =
        Expression.ElementInit(
            addMethod,
            Expression.Constant("Result1"));
    ElementInit elementInit2 =
        Expression.ElementInit(
            addMethod,
            Expression.Constant("Result2"));

    NewExpression newListExpression =
        Expression.New(typeof(List<string>));

    ListInitExpression listInitExpression =
        Expression.ListInit(
            newListExpression,
            elementInit1,
            elementInit2);

    Console.WriteLine(listInitExpression.ToString());
    Console.ReadLine();
}
Результат:
new List`1() {Void Add(System.String)("Result1"), Void Add(System.String)("Resul
t2")}
Примечание. В документации перед методом ListInit идет метод Expression.ListBind, который позволяет заменить какое-то значение с коллекции. Но поскольку в того же списка для изменения какого-то элемента есть только индексатор, я так и не смог придумать, как использовать этот метод. Примеров в интернете мне не удалось найти для данного случая, поэтому был вынужден пропустить использование этого метода.
Loop
Создает объект LoopExpression, представляющий собой бесконечный цикл, который может быть прерван с помощью оператора break.
Пример:
static void Main(string[] args)
{
    ParameterExpression value = Expression.Parameter(typeof(int), "value");

    // Creating an expression to hold a local variable.
    ParameterExpression result = Expression.Parameter(typeof(int), "result");

    // Creating a label to jump to from a loop.
    LabelTarget label = Expression.Label(typeof(int));

    // Creating a method body.
    BlockExpression block = Expression.Block(
        new[] { result },
        Expression.Assign(result, Expression.Constant(1)),
            Expression.Loop(
                Expression.IfThenElse(
                    Expression.GreaterThan(value, Expression.Constant(1)),
                    Expression.MultiplyAssign(result,
                        Expression.PostDecrementAssign(value)),
                    Expression.Break(label, result)
                ),
            label
        )
    );

    // Compile and run an expression tree.
    int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);

    Console.WriteLine(factorial);

    Console.ReadLine();
}
Результат:
120
MakeBinary
Создает BinaryExpression, содержащий бинарный оператор. Эта функция базовая, чтобы не использовать по отдельности функции по работе с BinaryExpression. Мы уже до этого с вами рассмотрели использование функций Add, And, AddChecked и т.д. Для них Microsoft реализовала высокоуровневую оболочку. 
public static BinaryExpression MakeBinary(ExpressionType binaryType, Expression left, Expression right, bool liftToNull, MethodInfo method, LambdaExpression conversion)
{
    switch (binaryType)
    {
        case ExpressionType.Add:
            return Add(left, right, method);
        case ExpressionType.AddChecked:
            return AddChecked(left, right, method);
        case ExpressionType.Subtract:
            return Subtract(left, right, method);
        case ExpressionType.SubtractChecked:
            return SubtractChecked(left, right, method);
        case ExpressionType.Multiply:
            return Multiply(left, right, method);
        case ExpressionType.MultiplyChecked:
            return MultiplyChecked(left, right, method);
        case ExpressionType.Divide:
            return Divide(left, right, method);
        case ExpressionType.Modulo:
            return Modulo(left, right, method);
        case ExpressionType.Power:
            return Power(left, right, method);
        case ExpressionType.And:
            return And(left, right, method);
        case ExpressionType.AndAlso:
            return AndAlso(left, right, method);
        case ExpressionType.Or:
            return Or(left, right, method);
        case ExpressionType.OrElse:
            return OrElse(left, right, method);
        case ExpressionType.LessThan:
            return LessThan(left, right, liftToNull, method);
        case ExpressionType.LessThanOrEqual:
            return LessThanOrEqual(left, right, liftToNull, method);
        case ExpressionType.GreaterThan:
            return GreaterThan(left, right, liftToNull, method);
        case ExpressionType.GreaterThanOrEqual:
            return GreaterThanOrEqual(left, right, liftToNull, method);
        case ExpressionType.Equal:
            return Equal(left, right, liftToNull, method);
        case ExpressionType.NotEqual:
            return NotEqual(left, right, liftToNull, method);
        case ExpressionType.ExclusiveOr:
            return ExclusiveOr(left, right, method);
        case ExpressionType.Coalesce:
            return Coalesce(left, right, conversion);
        case ExpressionType.ArrayIndex:
            return ArrayIndex(left, right);
        case ExpressionType.RightShift:
            return RightShift(left, right, method);
        case ExpressionType.LeftShift:
            return LeftShift(left, right, method);
        case ExpressionType.Assign:
            return Assign(left, right);
        case ExpressionType.AddAssign:
            return AddAssign(left, right, method, conversion);
        case ExpressionType.AndAssign:
            return AndAssign(left, right, method, conversion);
        case ExpressionType.DivideAssign:
            return DivideAssign(left, right, method, conversion);
        case ExpressionType.ExclusiveOrAssign:
            return ExclusiveOrAssign(left, right, method, conversion);
        case ExpressionType.LeftShiftAssign:
            return LeftShiftAssign(left, right, method, conversion);
        case ExpressionType.ModuloAssign:
            return ModuloAssign(left, right, method, conversion);
        case ExpressionType.MultiplyAssign:
            return MultiplyAssign(left, right, method, conversion);
        case ExpressionType.OrAssign:
            return OrAssign(left, right, method, conversion);
        case ExpressionType.PowerAssign:
            return PowerAssign(left, right, method, conversion);
        case ExpressionType.RightShiftAssign:
            return RightShiftAssign(left, right, method, conversion);
        case ExpressionType.SubtractAssign:
            return SubtractAssign(left, right, method, conversion);
        case ExpressionType.AddAssignChecked:
            return AddAssignChecked(left, right, method, conversion);
        case ExpressionType.SubtractAssignChecked:
            return SubtractAssignChecked(left, right, method, conversion);
        case ExpressionType.MultiplyAssignChecked:
            return MultiplyAssignChecked(left, right, method, conversion);
        default:
            throw Error.UnhandledBinary(binaryType);
    }
}
Пример:
static void Main(string[] args)
{
    Expression makeBinaryExpr = Expression.MakeBinary(ExpressionType.Add,
        Expression.Constant(4),
        Expression.Constant(2));
    Console.WriteLine(Expression.Lambda<Func<int>>(makeBinaryExpr).Compile()());

    Console.ReadLine();
}
Результат:
6
На этой позитивной ноте мы завершим данную статью. В следующей части темы рассмотрим такие операции, как умножение, преинкрементные и постинкрементные операции и многое другое.  

No comments:

Post a Comment