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 generatedParameterExpression
typeParameter()
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<Action> lambda = <Action>(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<Action<Test>> lambda = <Action<Test>>(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&lt;MemberBinding&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<MemberBinding>() { binding } ); // Call method MethodCallExpression method1 = ( test, typeof(Test).GetMethod("Print", new Type[] { typeof(string) }), ("Print out") ); // Call properties MemberExpression method2 = (test, "sample"); Expression<Action> lambda1 = <Action>(method1); ()(); Expression<Func<int>> lambda2 = <Func<int>>(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<string>(); (); } public static void RunExpression<T>() { // Call the constructor NewExpression newA = (typeof(Test<T>)); // Instantiate a type MemberInitExpression test = (newA, new List<MemberBinding>() { } ); // Call method MethodCallExpression method = ( test, typeof(Test<T>).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }), ("Print out") ); Expression<Action> lambda1 = <Action>(method); ()(); (); }
5. Define collection variables, initialize, and add elements
Collection type useListInitExpression
express.
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<string>).GetMethod("Add"); ElementInit add1 = (listAdd, ("a")); ElementInit add2 = (listAdd, ("b")); ElementInit add3 = (listAdd, ("666")); NewExpression list = (typeof(List<string>)); // 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<Func<int>> lambda = <Func<int>>(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.