您好,登錄后才能下訂單哦!
這篇文章主要介紹了C#值類型、引用類型、泛型、集合的表達式樹怎么創建的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇C#值類型、引用類型、泛型、集合的表達式樹怎么創建文章都會有所收獲,下面我們一起來看看吧。
C# 表達式樹中,定義一個變量,使用 ParameterExpression
。
創建變量結點的方法有兩種,
Expression.Parameter() Expression.Variable() // 另外,定義一個常量可以使用 Expression.Constant()。
兩種方式都是生成 ParameterExpression
類型 Parameter()
和 Variable()
都具有兩個重載。他們創建一個 ParameterExpression節點,該節點可用于標識表達式樹中的參數或變量。
對于使用定義:
Expression.Variable
用于在塊內聲明局部變量。
Expression.Parameter
用于聲明輸入值的參數。
先看第一種
public static ParameterExpression Parameter(Type type) { return Parameter(type, name: null); } public static ParameterExpression Variable(Type type) { return Variable(type, name: null); }
從代碼來看,沒有區別。
再看看具有兩個參數的重載
public static ParameterExpression Parameter(Type type, string name) { Validate(type, allowByRef: true); bool byref = type.IsByRef; if (byref) { type = type.GetElementType(); } return ParameterExpression.Make(type, name, byref); }
public static ParameterExpression Variable(Type type, string name) { Validate(type, allowByRef: false); return ParameterExpression.Make(type, name, isByRef: false); }
如你所見,兩者只有一個 allowByRef 出現了區別,Paramter 允許 Ref, Variable 不允許。
筆者在官方文檔和其他作者文章上,都沒有找到具體區別是啥,去 stackoverflow 搜索和查看源代碼后,確定他們的區別在于 Variable 不能使用 ref 類型。
從字面意思來看,聲明一個變量,應該用Expression.Variable
, 函數的傳入參數應該使用Expression.Parameter
。
無論值類型還是引用類型,都是這樣子定義。
訪問變量或類型的屬性,使用
Expression.Property()
訪問變量/類型的屬性或字段,使用
Expression.PropertyOrField()
訪問變量或類型的方法,使用
Expression.Call()
訪問屬性字段和方法
Expression.MakeMemberAccess
他們都返回一個 MemberExpression類型。
使用上,根據實例化/不實例化,有個小區別,上面說了變量或類型。
意思是,已經定義的值類型或實例化的引用類型,是變量;
類型,就是指引用類型,不需要實例化的靜態類型或者靜態屬性字段/方法。
上面的解釋不太嚴謹,下面示例會慢慢解釋。
使用 Expression.Property()
或 Expression.PropertyOrField()
調用屬性。
Console 是一個靜態類型,Console.Title 可以獲取編譯器程序的實際位置。
Console.WriteLine(Console.Title);
使用表達式樹表達如下
MemberExpression member = Expression.Property(null, typeof(Console).GetProperty("Title")); Expression<Func<string>> lambda = Expression.Lambda<Func<string>>(member); string result = lambda.Compile()(); Console.WriteLine(result); Console.ReadKey();
因為調用的是靜態類型的屬性,所以第一個參數為空。
第二個參數是一個 PropertyInfo 類型。
C#代碼如下
List<int> a = new List<int>() { 1, 2, 3 }; int result = a.Count; Console.WriteLine(result); Console.ReadKey();
在表達式樹,調用實例的屬性
ParameterExpression a = Expression.Parameter(typeof(List<int>), "a"); MemberExpression member = Expression.Property(a, "Count"); Expression<Func<List<int>, int>> lambda = Expression.Lambda<Func<List<int>, int>>(member, a); int result = lambda.Compile()(new List<int> { 1, 2, 3 }); Console.WriteLine(result); Console.ReadKey();
除了 Expression.Property() ,其他的方式請自行測試,這里不再贅述。
使用 Expression.Call()
可以調用一個靜態類型的函數或者實例的函數。
以 Console 為例,調用 WriteLine() 方法
Console.WriteLine("調用WriteLine方法"); MethodCallExpression method = Expression.Call( null, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), Expression.Constant("調用WriteLine方法")); Expression<Action> lambda = Expression.Lambda<Action>(method); lambda.Compile()(); Console.ReadKey();
Expression.Call() 的重載方法比較多,常用的重載方法是
public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)
因為要調用靜態類型的函數,所以第一個 instance 為空(instance英文意思是實例)。
第二個 method 是要調用的重載方法。
最后一個 arguments 是傳入的參數。
寫一個類
public class Test { public void Print(string info) { Console.WriteLine(info); } }
調用實例的 Printf() 方法
Test test = new Test(); test.Print("打印出來"); Console.ReadKey();
表達式表達如下
ParameterExpression a = Expression.Variable(typeof(Test), "test"); MethodCallExpression method = Expression.Call( a, typeof(Test).GetMethod("Print", new Type[] { typeof(string) }), Expression.Constant("打印出來") ); Expression<Action<Test>> lambda = Expression.Lambda<Action<Test>>(method,a); lambda.Compile()(new Test()); Console.ReadKey();
注意的是,Expression.Variable(typeof(Test), "test");
僅定義了一個變量,還沒有初始化/賦值。對于引用類型來說,需要實例化。
上面的方式,是通過外界實例化傳入里面的,后面會說如何在表達式內實例化。
引用類型的實例化,使用 new ,然后選擇調用合適的構造函數、設置屬性的值。
那么,根據上面的步驟,我們分開討論。
使用 Expression.New()
來調用一個類型的構造函數。
他有五個重載,有兩種常用重載:
public static NewExpression New(ConstructorInfo constructor); public static NewExpression New(Type type);
依然使用上面的 Test 類型
NewExpression newA = Expression.New(typeof(Test));
默認沒有參數的構造函數,或者只有一個構造函數,像上面這樣調用。
如果像指定一個構造函數,可以
NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));
這里就不詳細說了。
實例化一個構造函數的同時,可以給屬性賦值。
public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings); public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);
兩種重載是一樣的。
我們將 Test 類改成
public class Test { public int sample { get; set; } public void Print(string info) { Console.WriteLine(info); } }
然后
var binding = Expression.Bind( typeof(Test).GetMember("sample")[0], Expression.Constant(10) );
Expression.MemberInit()
表示調用構造函數并初始化新對象的一個或多個成員。
如果實例化一個類,可以使用
NewExpression newA = Expression.New(typeof(Test)); MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { } );
如果要在實例化時給成員賦值
NewExpression newA = Expression.New(typeof(Test)); // 給 Test 類型的一個成員賦值 var binding = Expression.Bind( typeof(Test).GetMember("sample")[0],Expression.Constant(10)); MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { binding} );
實例化一個類型,調用構造函數、給成員賦值,示例代碼如下
// 調用構造函數 NewExpression newA = Expression.New(typeof(Test)); // 給 Test 類型的一個成員賦值 var binding = Expression.Bind( typeof(Test).GetMember("sample")[0], Expression.Constant(10)); // 實例化一個類型 MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { binding } ); // 調用方法 MethodCallExpression method1 = Expression.Call( test, typeof(Test).GetMethod("Print", new Type[] { typeof(string) }), Expression.Constant("打印出來") ); // 調用屬性 MemberExpression method2 = Expression.Property(test, "sample"); Expression<Action> lambda1 = Expression.Lambda<Action>(method1); lambda1.Compile()(); Expression<Func<int>> lambda2 = Expression.Lambda<Func<int>>(method2); int sample = lambda2.Compile()(); Console.WriteLine(sample); Console.ReadKey();
將 Test 類,改成這樣
public class Test<T> { public void Print<T>(T info) { Console.WriteLine(info); } }
Test 類已經是一個泛型類,表達式實例化示例
static void Main(string[] args) { RunExpression<string>(); Console.ReadKey(); } public static void RunExpression<T>() { // 調用構造函數 NewExpression newA = Expression.New(typeof(Test<T>)); // 實例化一個類型 MemberInitExpression test = Expression.MemberInit(newA, new List<MemberBinding>() { } ); // 調用方法 MethodCallExpression method = Expression.Call( test, typeof(Test<T>).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }), Expression.Constant("打印出來") ); Expression<Action> lambda1 = Expression.Lambda<Action>(method); lambda1.Compile()(); Console.ReadKey(); }
集合類型使用 ListInitExpression
表示。
創建集合類型,需要使用到
ElementInit 表示 IEnumerable集合的單個元素的初始值設定項。
ListInit 初始化一個集合。
C# 中,集合都實現了 IEnumerable,集合都具有 Add 扥方法或屬性。
使用 C# 初始化一個集合并且添加元素,可以這樣
List<string> list = new List<string>() { "a", "b" }; list.Add("666");
而在表達式樹里面,是通過 ElementInit 調用 Add 方法初始化/添加元素的。
示例
MethodInfo listAdd = typeof(List<string>).GetMethod("Add"); /* * new List<string>() * { * "a", * "b" * }; */ ElementInit add1 = Expression.ElementInit( listAdd, Expression.Constant("a"), Expression.Constant("b") ); // Add("666") ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("666"));
示例
MethodInfo listAdd = typeof(List<string>).GetMethod("Add"); ElementInit add1 = Expression.ElementInit(listAdd, Expression.Constant("a")); ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("b")); ElementInit add3 = Expression.ElementInit(listAdd, Expression.Constant("666")); NewExpression list = Expression.New(typeof(List<string>)); // 初始化值 ListInitExpression setList = Expression.ListInit( list, add1, add2, add3 ); // 沒啥執行的,就這樣看看輸出的信息 Console.WriteLine(setList.ToString()); MemberExpression member = Expression.Property(setList, "Count"); Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(member); int result = lambda.Compile()(); Console.WriteLine(result); Console.ReadKey();
關于“C#值類型、引用類型、泛型、集合的表達式樹怎么創建”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“C#值類型、引用類型、泛型、集合的表達式樹怎么創建”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。