Expression Tree Practice: Expression Tree Representation of C# Five Operators

Catalog

Expression Tree Practice: C Operators

In C #, arithmetic operators have the following types

  • Arithmetic operator
  • Relational operator
  • Logical Operator
  • Bitwise Operators
  • Assignment Operators
  • Other Operators

These operators can be divided into unary operator, binary operator and ternary operator according to the number of parameters. This article will focus on these operators to demonstrate how to use the expression tree for operations.

The subtypes of Expression for unary and binary operators are as follows:

UnaryExpression; //Univariate Operational Expressions
BinaryExpression; //Binary Operational Expressions

First, arithmetic operators

operator describe
+ Add up two operands
- Subtract the second operand from the first operand
* Multiply two operands
/ Molecule divided by denominator
% Modular operator, integer division remainder
++ Self-incrementing operator, integer value increment 1
-- Self-decreasing operator, integer value reduced by 1

+ and Add()

Normal code

            int a;
            int b;
            a = 100;
            b = 200;
            var ab = a + b;
            Console.WriteLine(ab);

Using expression tree to build

            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // ab = a + b
            BinaryExpression ab = Expression.Add(a, b);

            // Print the value of a + b
            MethodCallExpression method = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), ab);

            Expression<Action<int, int>> lambda = Expression.Lambda<Action<int, int>>(method, a, b);
            lambda.Compile()(100, 200);

            Console.ReadKey();

If you want to be more complex, use blocks to perform:

            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // Don't forget assignment
            BinaryExpression aa = Expression.Assign(a, Expression.Constant(100, typeof(int)));
            BinaryExpression bb = Expression.Assign(b, Expression.Constant(200, typeof(int)));

            // ab = a + b
            BinaryExpression ab = Expression.Add(a, b);

            // Print the value of a + b
            MethodCallExpression method = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), ab);

            // Executing code in block form, equivalent to {}
            // There's no need to tangle here. I'll go into details later. The point is above.
            var call = Expression.Block(new ParameterExpression[] { a, b }, aa, bb, method);
            Expression<Action> lambda = Expression.Lambda<Action>(call);
            lambda.Compile()();

The above two examples use expression tree to compute the results, and then use expression tree to print the results.

The former relies on the input parameter values to assign a and b, while the latter uses expression tree assignment and operation.

So, how to perform the operation through the expression tree and get the execution result?

            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // ab = a + b
            BinaryExpression ab = Expression.Add(a, b);

            Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(ab, a, b);
            int result = lambda.Compile()(100, 200);

            Console.WriteLine(result);
            Console.ReadKey();

The difference is how to write Expression.Lambda().

Additionally, AddChecked() can be used to check for operation overflows.

- and Subtract()

Consistent with additions, SubtractChecked() checks for spillovers.

a - b, the result is 100.

            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // ab = a - b
            BinaryExpression ab = Expression.Subtract(a, b);

            Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(ab, a, b);
            int result = lambda.Compile()(200, 100);

            Console.WriteLine(result);

Multiplication, division and modularization

multiplication

            // ab = a * b
            BinaryExpression ab = Expression.Multiply(a, b);
// ab = 20000

division

            // ab = a / b
            BinaryExpression ab = Expression.Divide(a, b);
// ab = 2

Take mode (%)

            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // ab = a % b
            BinaryExpression ab = Expression.Modulo(a, b);

            Expression<Func<int, int, int>> lambda = Expression.Lambda<Func<int, int, int>>(ab, a, b);
            int result = lambda.Compile()(200, 150);
// ab = 50
            Console.WriteLine(result);
            Console.ReadKey();

Self increasing and decreasing

There are two models of self-increasing and self-decreasing, one is x++ or x--, the other is + + X or - X.

They all belong to the Unary Expression type.

Arithmetic operator Expression tree Explain
x++ Expression.PostIncrementAssign() Postposition
x-- Expression.PostDecrementAssign() Postposition
++x Expression.PreIncrementAssign() Preposition
--x Expression.PreDecrementAssign() Preposition

Keep in mind: Post is postpositioned, Pre is prepositioned; Increment is added, Decrement is subtracted; Assign is related to assignment (as will be mentioned later);

The Use of x++ and x--

            int a = 10;
            int b = 10;
            a++;
            b--;
            Console.WriteLine(a);
            Console.WriteLine(b);
            // int a,b;
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // a = 10,b = 10;
            BinaryExpression setA = Expression.Assign(a, Expression.Constant(10));
            BinaryExpression setB = Expression.Assign(b, Expression.Constant(10));

            // a++
            UnaryExpression aa = Expression.PostIncrementAssign(a);

            // b--
            UnaryExpression bb = Expression.PostDecrementAssign(b);

            //Console.WriteLine(a);
            //Console.WriteLine(b);
            MethodCallExpression callA = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a);
            MethodCallExpression callB = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), b);

            BlockExpression block = Expression.Block(
                new ParameterExpression[] { a, b },
                setA,
                setB,
                aa,
                bb,
                callA,
                callB
                );

            Expression<Action> lambda = Expression.Lambda<Action>(block);
            lambda.Compile()();

            Console.ReadKey();

If you want to pass parameters in from outside, set a, b

            // int a,b;
            ParameterExpression a = Expression.Variable(typeof(int), "a");
            ParameterExpression b = Expression.Variable(typeof(int), "b");

            // a++
            UnaryExpression aa = Expression.PostIncrementAssign(a);

            // b--
            UnaryExpression bb = Expression.PostDecrementAssign(b);

            //Console.WriteLine(a);
            //Console.WriteLine(b);
            MethodCallExpression callA = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a);
            MethodCallExpression callB = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), b);

            BlockExpression block = Expression.Block(
                aa,
                bb,
                callA,
                callB
                );

            Expression<Action<int, int>> lambda = Expression.Lambda<Action<int, int>>(block, a, b);
            lambda.Compile()(10, 10);
            Console.ReadKey();

The resulting expression tree is as follows

.Lambda #Lambda1<System.Action`2[System.Int32,System.Int32]>(
    System.Int32 $a,
    System.Int32 $b) {
    .Block() {
        $a++;
        $b--;
        .Call System.Console.WriteLine($a);
        .Call System.Console.WriteLine($b)
    }
}

To understand Expression.Block(), you can learn it here (Block () will be mentioned later).

            // int a,b;
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");
            ParameterExpression c = Expression.Variable(typeof(int), "c");

            BinaryExpression SetA = Expression.Assign(a, c);
            BinaryExpression SetB = Expression.Assign(b, c);
            // a++
            UnaryExpression aa = Expression.PostIncrementAssign(a);

            // b--
            UnaryExpression bb = Expression.PostDecrementAssign(b);

            //Console.WriteLine(a);
            //Console.WriteLine(b);
            MethodCallExpression callA = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a);
            MethodCallExpression callB = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), b);

            BlockExpression block = Expression.Block(
                new ParameterExpression[] { a, b },
                SetA,
                SetB,
                aa,
                bb,
                callA,
                callB
                );

            Expression<Action<int>> lambda = Expression.Lambda<Action<int>>(block, c);
            lambda.Compile()(10);

            Console.ReadKey();

Why is there an extra c here? Let's look at the generated expression tree

.Lambda #Lambda1<System.Action`1[System.Int32]>(System.Int32 $c) {
    .Block(
        System.Int32 $a,
        System.Int32 $b) {
        $a = $c;
        $b = $c;
        $a++;
        $b--;
        .Call System.Console.WriteLine($a);
        .Call System.Console.WriteLine($b)
    }
}

Look at the expression tree generated by the following code

            // int a,b;
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // a++
            UnaryExpression aa = Expression.PostIncrementAssign(a);

            // b--
            UnaryExpression bb = Expression.PostDecrementAssign(b);

            //Console.WriteLine(a);
            //Console.WriteLine(b);
            MethodCallExpression callA = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), a);
            MethodCallExpression callB = Expression.Call(null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) }), b);

            BlockExpression block = Expression.Block(
                new ParameterExpression[] { a, b },
                aa,
                bb,
                callA,
                callB
                );

            Expression<Action<int, int>> lambda = Expression.Lambda<Action<int, int>>(block, a, b);
            lambda.Compile()(10, 10);
            Console.ReadKey();
.Lambda #Lambda1<System.Action`2[System.Int32,System.Int32]>(
    System.Int32 $a,
    System.Int32 $b) {
    .Block(
        System.Int32 $a,
        System.Int32 $b) {
        $a++;
        $b--;
        .Call System.Console.WriteLine($a);
        .Call System.Console.WriteLine($b)
    }
}

As for the self-increasing and self-decreasing of the front-end, we can write it according to the example above, but it should be noted that + + x and - x are "operation before increase/decrease".

2. Relational operators

==,!=,>,<,>=,<=

The relational operators in C # are as follows

operator describe
== Check that the values of the two operands are equal, and if they are equal, the condition is true.
!= Check that the values of the two operands are equal, and if they are not equal, the condition is true.
> Check whether the value of the left operand is greater than the value of the right operand, and if so, the condition is true.
< Check whether the value of the left operand is less than the value of the right operand, and if so, the condition is true.
>= Check whether the value of the left operand is greater than or equal to the value of the right operand, and if so, the condition is true.
<= Check whether the value of the left operand is less than or equal to the value of the right operand, and if so, the condition is true.

== Represents an equal comparison, if it is a value type and string type, whether the comparison value is the same; if it is a reference type, whether the reference address is equal.

Other relational operators compare only the size of the value type.

Example code

            int a = 21;
            int b = 10;
            Console.Write("a == b: ");
            Console.WriteLine(a == b);

            Console.Write("a < b : ");
            Console.WriteLine(a < b);


            Console.Write("a > b : ");
            Console.WriteLine(a > b);

            // Change the values of a and b 
            a = 5;
            b = 20;

            Console.Write("a <= b: ");
            Console.WriteLine(a <= b);


            Console.Write("a >= b: ");
            Console.WriteLine(b >= a);

            Console.ReadKey();

Using expression tree to implement

            // int a,b;
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            // a = 21,b = 10;
            BinaryExpression setA = Expression.Assign(a, Expression.Constant(21));
            BinaryExpression setB = Expression.Assign(b, Expression.Constant(20));

            // Console.Write("a == b: ");
            // Console.WriteLine(a == b);
            MethodCallExpression call1 = Expression.Call(null,
                typeof(Console).GetMethod("Write", new Type[] { typeof(string) }),
                Expression.Constant("a == b: "));
            MethodCallExpression call11 = Expression.Call(null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.Equal(a, b));

            // Console.Write("a < b : ");
            // Console.WriteLine(a < b);
            MethodCallExpression call2 = Expression.Call(null,
                typeof(Console).GetMethod("Write", new Type[] { typeof(string) }),
                Expression.Constant("a < b : "));
            MethodCallExpression call22 = Expression.Call(null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.LessThan(a, b));

            // Console.Write("a > b : ");
            // Console.WriteLine(a > b);
            MethodCallExpression call3 = Expression.Call(null,
                typeof(Console).GetMethod("Write", new Type[] { typeof(string) }),
                Expression.Constant("a > b : "));
            MethodCallExpression call33 = Expression.Call(null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.GreaterThan(a, b));


            // Change the values of a and b 
            // a = 5;
            // b = 20;
            BinaryExpression setAa = Expression.Assign(a, Expression.Constant(5));
            BinaryExpression setBb = Expression.Assign(b, Expression.Constant(20));

            // Console.Write("a <= b: ");
            // Console.WriteLine(a <= b);
            MethodCallExpression call4 = Expression.Call(null,
                typeof(Console).GetMethod("Write", new Type[] { typeof(string) }),
                Expression.Constant("a <= b: "));
            MethodCallExpression call44 = Expression.Call(null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.LessThanOrEqual(a, b));

            // Console.Write("a >= b: ");
            // Console.WriteLine(b >= a);
            MethodCallExpression call5 = Expression.Call(null,
                typeof(Console).GetMethod("Write", new Type[] { typeof(string) }),
                Expression.Constant("a >= b: "));
            MethodCallExpression call55 = Expression.Call(null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.GreaterThanOrEqual(a, b));

            BlockExpression block = Expression.Block(new ParameterExpression[] { a, b },
                setA,
                setB,
                call1,
                call11,
                call2,
                call22,
                call3,
                call33,
                setAa,
                setBb,
                call4,
                call44,
                call5,
                call55
                );

            Expression<Action> lambda = Expression.Lambda<Action>(block);
            lambda.Compile()();
            Console.ReadKey();

The resulting expression tree is as follows

.Lambda #Lambda1<System.Action>() {
    .Block(
        System.Int32 $a,
        System.Int32 $b) {
        $a = 21;
        $b = 20;
        .Call System.Console.Write("a == b: ");
        .Call System.Console.WriteLine($a == $b);
        .Call System.Console.Write("a < b : ");
        .Call System.Console.WriteLine($a < $b);
        .Call System.Console.Write("a > b : ");
        .Call System.Console.WriteLine($a > $b);
        $a = 5;
        $b = 20;
        .Call System.Console.Write("a <= b: ");
        .Call System.Console.WriteLine($a <= $b);
        .Call System.Console.Write("a >= b: ");
        .Call System.Console.WriteLine($a >= $b)
    }
}

3. Logical Operators

&&,||,!

operator describe
&& It is called logic and operator. If both operands are non-zero, the condition is true.
|| It is called logic or operator. If any of the two operands is non-zero, the condition is true.
! It is called a logical non-operator. Used to reverse the logical state of operands. If the condition is true, the logical non-operator will make it false.

The result of the operation of the logical operator is true or false.

Logical Operator Expression tree
&& Expression.AndAlso()
|| Expression.OrElse()
! Expression.Not()
            int a = 10;
            int b = 11;

            Console.Write("[a == b && a > b]: ");
            Console.WriteLine(a == b && a > b);

            Console.Write("[a > b || a == b]: ");
            Console.WriteLine(a > b || a == b);

            Console.Write("[!(a == b)]: ");
            Console.WriteLine(!(a == b));
            Console.ReadKey();

Writing with expression tree

            //int a = 10;
            //int b = 11;
            ParameterExpression a = Expression.Parameter(typeof(int), "a");
            ParameterExpression b = Expression.Parameter(typeof(int), "b");
            BinaryExpression setA = Expression.Assign(a, Expression.Constant(10));
            BinaryExpression setB = Expression.Assign(b, Expression.Constant(11));

            //Console.Write("[a == b && a > b]: ");
            //Console.WriteLine(a == b && a > b);
            MethodCallExpression call1 = Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("[a == b && a > b]: "));

            MethodCallExpression call2 = Expression.Call(
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                 Expression.AndAlso(Expression.Equal(a, b), Expression.GreaterThan(a, b))
                );

            //Console.Write("[a > b || a == b]: ");
            //Console.WriteLine(a > b || a == b);
            MethodCallExpression call3 = Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("[a > b || a == b]: "));
            MethodCallExpression call4 = Expression.Call(
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.OrElse(Expression.Equal(a, b), Expression.GreaterThan(a, b))
                );

            //Console.Write("[!(a == b)]: ");
            //Console.WriteLine(!(a == b));
            MethodCallExpression call5 = Expression.Call(null, typeof(Console).GetMethod("Write", new Type[] { typeof(string) }), Expression.Constant("[!(a == b)]: "));
            MethodCallExpression call6 = Expression.Call(
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(bool) }),
                Expression.Not(Expression.Equal(a, b))
                );
            BlockExpression block = Expression.Block(
                new ParameterExpression[] { a, b },
                setA,
                setB,
                call1,
                call2,
                call3,
                call4,
                call5,
                call6
                );

            Expression<Action> lambda = Expression.Lambda<Action>(block);
            lambda.Compile()();
            Console.ReadKey();

The resulting expression tree is as follows

.Lambda #Lambda1<System.Action>() {
    .Block(
        System.Int32 $a,
        System.Int32 $b) {
        $a = 10;
        $b = 11;
        .Call System.Console.Write("[a == b && a > b]: ");
        .Call System.Console.WriteLine($a == $b && $a > $b);
        .Call System.Console.Write("[a > b || a == b]: ");
        .Call System.Console.WriteLine($a == $b || $a > $b);
        .Call System.Console.Write("[!(a == b)]: ");
        .Call System.Console.WriteLine(!($a == $b))
    }
}

4. Bit Operator

&,|,^,~,<<,>>

operator describe Example
& If it exists in both operands, the binary AND operator copies one bit into the result. (A & B) will get 12, or 0000 1100.
| If it exists in any operand, the binary OR operator copies one bit into the result. (A | B) will get 61, or 0011 1101.
^ If it exists in one of the operands but not in two, the binary exclusive or operator copies one bit into the result. (A ^ B) will get 49, or 0011 0001.
~ Bit-by-bit inverse operators are unary operators with the effect of "flipping" bits, i.e. 0 becomes 1, 1 becomes 0, including symbol bits. (~A) will get - 61, that is, 1100011, a complement form of signed binary numbers.
<< Binary left shift operator. The value of the left operand moves to the left the number of digits specified by the right operand. A << 2 will get 240, or 111 0000.
>> Binary right shift operator. The value of the left operand moves to the right the number of digits specified by the right operand. A >> 2 will get 15, or 0000 1111.

Given the space, I'll write some examples.

Bitwise Operators Expression tree
& Expression.Add(Expression left, Expression right)
| Expression.Or(Expression left, Expression right)
^ Expression.ExclusiveOr(Expression expression)
~ Expression.OnesComplement( Expression expression)
<< Expression.LeftShift(Expression left, Expression right)
>> Expression.RightShift(Expression left, Expression right)

Five, assignment operator

operator describe Example
= A simple assignment operator assigns the value of the right operand to the left operand C = A + B assigns the value of A + B to C
+= Add and assign operators, assign the results of the right and left operands to the left operands C+= A equals C = C + A
-= Subtract the assignment operator and assign the result of subtracting the left operand from the right operand to the left operand C -= A equals C = C - A
*= Multiplier and assign operators, assign the result of multiplying the right operand by the left operand to the left operand C = A equals C = C A
/= By dividing and assigning operators, assign the result of dividing the left operand by the right operand to the left operand C/= A is equivalent to C = C/A
%= Find the modulus and assign the operator. Find the modulus of two operands and assign it to the left operand. C% = A equals C = C% = A
<<= Left shift and assignment operator C <= 2 is equivalent to C = C << 2
>>= Right Shift and Assignment Operator C >>= 2 is equivalent to C = C >> 2
&= Bit-sum and assignment operator C &= 2 is equivalent to C = C & 2
^= Bit exclusive or assignment operator C ^= 2 is equivalent to C = C ^ 2
|= Bit-by-bit or assignment operator C |= 2 is equivalent to C = C | 2

Limited to space, please appreciate it by yourself.

operator Expression tree
= Expression.Assign
+= Expression.AddAssign
-= Expression.SubtractAssign
*= Expression.MultiplyAssign
/= Expression.DivideAssign
%= Expression.ModuloAssign
<<= Expression.LeftShiftAssign
>>= Expression.RightShiftAssign
&= Expression.AndAssign
^= Expression.ExclusiveOrAssign
|= Expression.OrAssign

^= Notice that there are two meanings: Exclusive OrAssign and Power Assign.

Six, other operators

operator describe Example
sizeof() Returns the size of the data type. sizeof(int), which returns 4.
typeof() Returns the type of class. typeof(StreamReader);
& Returns the address of the variable. & a; the actual address of the variable will be obtained.
* A pointer to a variable. * a; will point to a variable.
? : Conditional expression If the condition is true, then X: Otherwise Y
is Determine whether the object is of a certain type. If (Ford is Car) // Check if Ford is an object of the Car class.
as Mandatory conversion does not throw an exception even if the conversion fails. Object obj = new StringReader("Hello"); StringReader r = obj as StringReader;

I haven't found how to write these operators in the expression tree. If you find them, please let me know...

Keywords: C# Lambda less

Added by deurwaarder on Wed, 18 Sep 2019 16:40:34 +0300