SoFunction
Updated on 2025-03-01

Expression tree practice for C# value types, reference types, generics, collections, and calling functions

1. Define variables

In the C# expression tree, define a variable and useParameterExpression

There are two ways to create variable nodes.

()
()
// in addition,Define a constant that can be used ()。

Both methods are generatedParameterExpressiontypeParameter()andVariable()Both have two overloads. They create a ParameterExpression node that can be used to identify parameters or variables in the expression tree.

For the usage definition:

Used to declare local variables within a block.

Parameters used to declare input values.

Look at the first one

        public static ParameterExpression Parameter(Type type)
        {
            return Parameter(type, name: null);
        }
        
                public static ParameterExpression Variable(Type type)
        {
            return Variable(type, name: null);
        }

From the code point of view, there is no difference.

Let's take a look at the overload with two parameters

        public static ParameterExpression Parameter(Type type, string name)
        {
            Validate(type, allowByRef: true);
            bool byref = ;
            if (byref)
            {
                type = ();
            }

            return (type, name, byref);
        }
        public static ParameterExpression Variable(Type type, string name)
        {
            Validate(type, allowByRef: false);
            return (type, name, isByRef: false);
        }

As you can see, there is a difference between the two that only one allowsByRef, Paramter allows Ref, Variable does not.

The author has not found the specific difference in the official documents and other author articles. After searching and viewing the source code in *, we have determined that the difference is that Variable cannot use the ref type.

From the literal point of view, declaring a variable should use, the function passed in parameters should be used

This is the definition of both value type and reference type.

2. Access variable/type attribute fields and methods

Access variable or type attributes, use

()

Access attributes or fields of variables/types, using

()

Methods to access variables or types, use

()

Access attribute fields and methods

They all return a MemberExpression type.

In terms of use, based on instantiation/not instantiation, there is a small difference, which talks about variables or types.

Meaning that the value type that has been defined or the instantiated reference type is a variable;

Type refers to the reference type, and does not require instantiation of static types or static attribute fields/methods.

The above explanation is not very rigorous, and the following example will be explained slowly.

1. Access properties

use()or()Call properties.

Calling static typed properties

Console is a static type that can get the actual location of the compiler program.

            ();

Use the expression tree to express it as follows

            MemberExpression member = (null, typeof(Console).GetProperty("Title"));
            Expression<Func<string>> lambda = <Func<string>>(member);

            string result = ()();
            (result);

            ();

Because the statically typed property is called, the first parameter is empty.

The second parameter is a PropertyInfo type.

Call instance properties/fields

The C# code is as follows

            List<int> a = new List<int>() { 1, 2, 3 };
            int result = ;
            (result);
            ();

In the expression tree, call the instance's properties

            ParameterExpression a = (typeof(List<int>), "a");
            MemberExpression member = (a, "Count");

            Expression<Func<List<int>, int>> lambda = <Func<List<int>, int>>(member, a);
            int result = ()(new List<int> { 1, 2, 3 });
            (result);

            ();

Except (), please test the other methods yourself, and I won’t go into details here.

2. Calling the function

use()You can call a statically typed function or an instance function.

Call statically typed functions

Taking Console as an example, call the WriteLine() method

            ("Calling WriteLine method");

            MethodCallExpression method = (
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
                ("Calling WriteLine method"));

            Expression&lt;Action&gt; lambda = &lt;Action&gt;(method);
            ()();
            ();

There are many overload methods in () and the commonly used overload methods are

public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)

Because the statically typed function is called, the first instance is empty (instance means instance in English).

The second method is the overloaded method to be called.

The last argument is the passed parameter.

Calling the instance's function

Write a class

    public class Test
    {
        public void Print(string info)
        {
            (info);
        }
    }

Calling the Printf() method of the instance

            Test test = new Test();
            ("Print out");
            ();

The expression is expressed as follows

            ParameterExpression a = (typeof(Test), "test");

            MethodCallExpression method = (
                a,
                typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
                ("Print out")
                );

            Expression&lt;Action&lt;Test&gt;&gt; lambda = &lt;Action&lt;Test&gt;&gt;(method,a);
            ()(new Test());
            ();

Note that(typeof(Test), "test");Only one variable is defined and has not been initialized/assigned yet. For reference types, instantiation is required.

The above method is passed into it through external instantiation, and later we will talk about how to instantiate it in the expression.

Three, instantiate the reference type

Refer to the instantiation of the type, use new , and then select to call the appropriate constructor and set the value of the property.

So, based on the above steps, we will discuss it separately.

new

use()To call a type constructor.

It has five overloads and two commonly used overloads:

 public static NewExpression New(ConstructorInfo constructor);
 public static NewExpression New(Type type);

Still use the Test type above

            NewExpression newA = (typeof(Test));

By default, there is no constructor with parameters, or there is only one constructor, called like above.

If you specify a constructor like

            NewExpression newA = (typeof(Test).GetConstructor(xxxxxx));

I won't go into details here.

Assign values ​​to attributes

While instantiating a constructor, you can assign values ​​to attributes.

        public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings);

        public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);

The two overloads are the same.

We change the Test class to

    public class Test
    {
        public int sample { get; set; }
        public void Print(string info)
        {
            (info);
        }
    }

Then

            var binding = (
                typeof(Test).GetMember("sample")[0],
                (10)
            );

Create a reference type

()

Indicates that one or more members of a new object are called and initialized.

If you instantiate a class, you can use

            NewExpression newA = (typeof(Test));
            MemberInitExpression test = (newA,
                new List<MemberBinding>() { }
                );

If you want to assign a value to a member during instantiation

            NewExpression newA = (typeof(Test));

            // Assign a value to a member of the Test type            var binding = (
                typeof(Test).GetMember("sample")[0],(10));

            MemberInitExpression test = (newA,
                new List&amp;lt;MemberBinding&amp;gt;() { binding}
                );

Example

Instantiate a type, call the constructor, assign values ​​to members. The example code is as follows

            // Call the constructor            NewExpression newA = (typeof(Test));

            // Assign a value to a member of the Test type            var binding = (
                typeof(Test).GetMember("sample")[0], (10));

            // Instantiate a type            MemberInitExpression test = (newA,
                new List&lt;MemberBinding&gt;() { binding }
                );

            // Call method            MethodCallExpression method1 = (
                test,
                typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
                ("Print out")
                );

            // Call properties            MemberExpression method2 = (test, "sample");

            Expression&lt;Action&gt; lambda1 = &lt;Action&gt;(method1);
            ()();

            Expression&lt;Func&lt;int&gt;&gt; lambda2 = &lt;Func&lt;int&gt;&gt;(method2);
            int sample = ()();
            (sample);

            ();

4. Instantiate generic types in the call

Change the Test class to this

    public class Test<T>
    {
        public void Print<T>(T info)
        {
            (info);
        }
    }

The Test class is already a generic class, an example of instantiating expressions

        static void Main(string[] args)
        {
            RunExpression&lt;string&gt;();
            ();
        }
        public static void RunExpression&lt;T&gt;()
        {
            // Call the constructor            NewExpression newA = (typeof(Test&lt;T&gt;));

            // Instantiate a type            MemberInitExpression test = (newA,
                new List&lt;MemberBinding&gt;() { }
                );

            // Call method            MethodCallExpression method = (
                test,
                typeof(Test&lt;T&gt;).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }),
                ("Print out")
                );

            Expression&lt;Action&gt; lambda1 = &lt;Action&gt;(method);
            ()();

            ();
        }

5. Define collection variables, initialize, and add elements

Collection type useListInitExpressionexpress.

Create a collection type, you need to use it

ElementInit represents the initializer for a single element of the IEnumerable collection.

ListInit Initizes a collection.

In C#, all collections implement IEnumerable, and all collections have Add methods or attributes.

Initialize a collection and add elements using C#, you can

            List<string> list = new List<string>()
            {
                "a",
                "b"
            };
            ("666");

In the expression tree, elements are initialized/added by calling the Add method by ElementInit.

Example

            MethodInfo listAdd = typeof(List<string>).GetMethod("Add");

            /*
             * new List<string>()
             * {
             *     "a",
             *     "b"
             * };
             */
            ElementInit add1 = (
                listAdd,
                ("a"),
                ("b")
                );
            // Add("666")
            ElementInit add2 = (listAdd, ("666"));

Example

            MethodInfo listAdd = typeof(List&lt;string&gt;).GetMethod("Add");

            ElementInit add1 = (listAdd, ("a"));
            ElementInit add2 = (listAdd, ("b"));
            ElementInit add3 = (listAdd, ("666"));

            NewExpression list = (typeof(List&lt;string&gt;));

            // Initialize the value            ListInitExpression setList = (
                list,
                add1,
                add2,
                add3
                );
            // There is nothing to execute, just look at the output information            (());

            MemberExpression member = (setList, "Count");

            Expression&lt;Func&lt;int&gt;&gt; lambda = &lt;Func&lt;int&gt;&gt;(member);
            int result = ()();
            (result);

            ();

This is what this article about expression tree practices for C# value types, reference types, generics, collections, and calling functions. I hope it will be helpful to everyone's learning and I hope everyone will support me more.