C#值類型、引用類型、泛型、集合、調(diào)用函數(shù)的表達(dá)式樹實(shí)踐
一,定義變量
C# 表達(dá)式樹中,定義一個(gè)變量,使用 ParameterExpression。
創(chuàng)建變量結(jié)點(diǎn)的方法有兩種,
Expression.Parameter() Expression.Variable() // 另外,定義一個(gè)常量可以使用 Expression.Constant()。
兩種方式都是生成 ParameterExpression 類型 Parameter() 和 Variable() 都具有兩個(gè)重載。他們創(chuàng)建一個(gè) ParameterExpression節(jié)點(diǎn),該節(jié)點(diǎn)可用于標(biāo)識(shí)表達(dá)式樹中的參數(shù)或變量。
對(duì)于使用定義:
Expression.Variable 用于在塊內(nèi)聲明局部變量。
Expression.Parameter用于聲明輸入值的參數(shù)。
先看第一種
public static ParameterExpression Parameter(Type type)
{
return Parameter(type, name: null);
}
public static ParameterExpression Variable(Type type)
{
return Variable(type, name: null);
}
從代碼來看,沒有區(qū)別。
再看看具有兩個(gè)參數(shù)的重載
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);
}
如你所見,兩者只有一個(gè) allowByRef 出現(xiàn)了區(qū)別,Paramter 允許 Ref, Variable 不允許。
筆者在官方文檔和其他作者文章上,都沒有找到具體區(qū)別是啥,去 stackoverflow 搜索和查看源代碼后,確定他們的區(qū)別在于 Variable 不能使用 ref 類型。
從字面意思來看,聲明一個(gè)變量,應(yīng)該用Expression.Variable, 函數(shù)的傳入?yún)?shù)應(yīng)該使用Expression.Parameter。
無論值類型還是引用類型,都是這樣子定義。
二,訪問變量/類型的屬性字段和方法
訪問變量或類型的屬性,使用
Expression.Property()
訪問變量/類型的屬性或字段,使用
Expression.PropertyOrField()
訪問變量或類型的方法,使用
Expression.Call()
訪問屬性字段和方法
Expression.MakeMemberAccess
他們都返回一個(gè) MemberExpression類型。
使用上,根據(jù)實(shí)例化/不實(shí)例化,有個(gè)小區(qū)別,上面說了變量或類型。
意思是,已經(jīng)定義的值類型或?qū)嵗囊妙愋?,是變量?/p>
類型,就是指引用類型,不需要實(shí)例化的靜態(tài)類型或者靜態(tài)屬性字段/方法。
上面的解釋不太嚴(yán)謹(jǐn),下面示例會(huì)慢慢解釋。
1. 訪問屬性
使用 Expression.Property() 或 Expression.PropertyOrField()調(diào)用屬性。
調(diào)用靜態(tài)類型屬性
Console 是一個(gè)靜態(tài)類型,Console.Title 可以獲取編譯器程序的實(shí)際位置。
Console.WriteLine(Console.Title);
使用表達(dá)式樹表達(dá)如下
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();因?yàn)檎{(diào)用的是靜態(tài)類型的屬性,所以第一個(gè)參數(shù)為空。
第二個(gè)參數(shù)是一個(gè) PropertyInfo 類型。
調(diào)用實(shí)例屬性/字段
C#代碼如下
List<int> a = new List<int>() { 1, 2, 3 };
int result = a.Count;
Console.WriteLine(result);
Console.ReadKey();在表達(dá)式樹,調(diào)用實(shí)例的屬性
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() ,其他的方式請(qǐng)自行測(cè)試,這里不再贅述。
2. 調(diào)用函數(shù)
使用 Expression.Call() 可以調(diào)用一個(gè)靜態(tài)類型的函數(shù)或者實(shí)例的函數(shù)。
調(diào)用靜態(tài)類型的函數(shù)
以 Console 為例,調(diào)用 WriteLine() 方法
Console.WriteLine("調(diào)用WriteLine方法");
MethodCallExpression method = Expression.Call(
null,
typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
Expression.Constant("調(diào)用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)
因?yàn)橐{(diào)用靜態(tài)類型的函數(shù),所以第一個(gè) instance 為空(instance英文意思是實(shí)例)。
第二個(gè) method 是要調(diào)用的重載方法。
最后一個(gè) arguments 是傳入的參數(shù)。
調(diào)用實(shí)例的函數(shù)
寫一個(gè)類
public class Test
{
public void Print(string info)
{
Console.WriteLine(info);
}
}調(diào)用實(shí)例的 Printf() 方法
Test test = new Test();
test.Print("打印出來");
Console.ReadKey();表達(dá)式表達(dá)如下
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"); 僅定義了一個(gè)變量,還沒有初始化/賦值。對(duì)于引用類型來說,需要實(shí)例化。
上面的方式,是通過外界實(shí)例化傳入里面的,后面會(huì)說如何在表達(dá)式內(nèi)實(shí)例化。
三,實(shí)例化引用類型
引用類型的實(shí)例化,使用 new ,然后選擇調(diào)用合適的構(gòu)造函數(shù)、設(shè)置屬性的值。
那么,根據(jù)上面的步驟,我們分開討論。
new
使用 Expression.New()來調(diào)用一個(gè)類型的構(gòu)造函數(shù)。
他有五個(gè)重載,有兩種常用重載:
public static NewExpression New(ConstructorInfo constructor); public static NewExpression New(Type type);
依然使用上面的 Test 類型
NewExpression newA = Expression.New(typeof(Test));
默認(rèn)沒有參數(shù)的構(gòu)造函數(shù),或者只有一個(gè)構(gòu)造函數(shù),像上面這樣調(diào)用。
如果像指定一個(gè)構(gòu)造函數(shù),可以
NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));
這里就不詳細(xì)說了。
給屬性賦值
實(shí)例化一個(gè)構(gòu)造函數(shù)的同時(shí),可以給屬性賦值。
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)
);創(chuàng)建引用類型
Expression.MemberInit()
表示調(diào)用構(gòu)造函數(shù)并初始化新對(duì)象的一個(gè)或多個(gè)成員。
如果實(shí)例化一個(gè)類,可以使用
NewExpression newA = Expression.New(typeof(Test));
MemberInitExpression test = Expression.MemberInit(newA,
new List<MemberBinding>() { }
);如果要在實(shí)例化時(shí)給成員賦值
NewExpression newA = Expression.New(typeof(Test));
// 給 Test 類型的一個(gè)成員賦值
var binding = Expression.Bind(
typeof(Test).GetMember("sample")[0],Expression.Constant(10));
MemberInitExpression test = Expression.MemberInit(newA,
new List<MemberBinding>() { binding}
);
示例
實(shí)例化一個(gè)類型,調(diào)用構(gòu)造函數(shù)、給成員賦值,示例代碼如下
// 調(diào)用構(gòu)造函數(shù)
NewExpression newA = Expression.New(typeof(Test));
// 給 Test 類型的一個(gè)成員賦值
var binding = Expression.Bind(
typeof(Test).GetMember("sample")[0], Expression.Constant(10));
// 實(shí)例化一個(gè)類型
MemberInitExpression test = Expression.MemberInit(newA,
new List<MemberBinding>() { binding }
);
// 調(diào)用方法
MethodCallExpression method1 = Expression.Call(
test,
typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
Expression.Constant("打印出來")
);
// 調(diào)用屬性
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();四,實(shí)例化泛型類型于調(diào)用
將 Test 類,改成這樣
public class Test<T>
{
public void Print<T>(T info)
{
Console.WriteLine(info);
}
}Test 類已經(jīng)是一個(gè)泛型類,表達(dá)式實(shí)例化示例
static void Main(string[] args)
{
RunExpression<string>();
Console.ReadKey();
}
public static void RunExpression<T>()
{
// 調(diào)用構(gòu)造函數(shù)
NewExpression newA = Expression.New(typeof(Test<T>));
// 實(shí)例化一個(gè)類型
MemberInitExpression test = Expression.MemberInit(newA,
new List<MemberBinding>() { }
);
// 調(diào)用方法
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表示。
創(chuàng)建集合類型,需要使用到
ElementInit 表示 IEnumerable集合的單個(gè)元素的初始值設(shè)定項(xiàng)。
ListInit 初始化一個(gè)集合。
C# 中,集合都實(shí)現(xiàn)了 IEnumerable,集合都具有 Add 扥方法或?qū)傩浴?/p>
使用 C# 初始化一個(gè)集合并且添加元素,可以這樣
List<string> list = new List<string>()
{
"a",
"b"
};
list.Add("666");而在表達(dá)式樹里面,是通過 ElementInit 調(diào)用 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
);
// 沒啥執(zhí)行的,就這樣看看輸出的信息
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();到此這篇關(guān)于C#值類型、引用類型、泛型、集合、調(diào)用函數(shù)的表達(dá)式樹實(shí)踐的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- C#表達(dá)式樹基礎(chǔ)教程
- C#循環(huán)與循環(huán)控制的表達(dá)式樹實(shí)現(xiàn)
- C#判斷語句的表達(dá)式樹實(shí)現(xiàn)
- C#五類運(yùn)算符使用表達(dá)式樹進(jìn)行操作
- C#使用表達(dá)式樹(LambdaExpression)動(dòng)態(tài)更新類的屬性值(示例代碼)
- C#表達(dá)式樹講解
- C#使用表達(dá)式樹實(shí)現(xiàn)對(duì)象復(fù)制的示例代碼
- C#表達(dá)式樹Expression基礎(chǔ)講解
- C# Lambda表達(dá)式及Lambda表達(dá)式樹的創(chuàng)建過程
- C#用表達(dá)式樹構(gòu)建動(dòng)態(tài)查詢的方法
- C#表達(dá)式樹的基本用法講解
- 淺談c#表達(dá)式樹Expression簡(jiǎn)單類型比較demo
- C# 表達(dá)式樹Expression Trees的知識(shí)梳理
- C#之Expression表達(dá)式樹實(shí)例
相關(guān)文章
C# 泛型數(shù)組學(xué)習(xí)小結(jié)
C# 泛型數(shù)組學(xué)習(xí)中我們需要注意什么事項(xiàng)呢?C# 泛型數(shù)組的使用又是如何呢?那么本文就向你詳細(xì)介紹這方面的內(nèi)容2012-09-09
Unity實(shí)現(xiàn)汽車前后輪倒車軌跡計(jì)算
這篇文章主要為大家詳細(xì)介紹了Unity實(shí)現(xiàn)汽車前后輪倒車軌跡計(jì)算,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
C#的FileSystemWatcher用法實(shí)例詳解
這篇文章主要介紹了C#的FileSystemWatcher用法,以實(shí)例形似詳細(xì)分析了FileSystemWatcher控件主要功能,并總結(jié)了FileSystemWatcher控件使用的技巧,需要的朋友可以參考下2014-11-11
C#環(huán)形隊(duì)列的實(shí)現(xiàn)方法詳解
這篇文章先是簡(jiǎn)單的給大家介紹了什么是環(huán)形隊(duì)列和環(huán)形隊(duì)列的優(yōu)點(diǎn),然后通過實(shí)例代碼給大家介紹C#如何實(shí)現(xiàn)環(huán)形隊(duì)列,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-09-09
C#中Thread(線程)和Task(任務(wù))實(shí)例詳解
.NET Framework在System.Threading命名空間中具有與線程相關(guān)的類,線程是一小組可執(zhí)行指令,這篇文章主要給大家介紹了關(guān)于C#中Thread(線程)和Task(任務(wù))的相關(guān)資料,需要的朋友可以參考下2022-03-03
解決C#中Linq GroupBy 和OrderBy失效的方法
最近發(fā)現(xiàn)了一個(gè)問題,在服務(wù)器端的Linq GroupBy 和OrderBy居然不管用,后來終于解決了所以現(xiàn)在分享給大家,有需要的朋友們可以參考借鑒。2016-09-09

