前言
本節(jié)重點不講反射機(jī)制,而是講lambda表達(dá)式樹來替代反射中常用的獲取屬性和方法,來達(dá)到相同的效果但卻比反射高效。
每個人都知道,用反射調(diào)用一個方法或者對屬性執(zhí)行setvalue和getvalue操作的時候都會比直接調(diào)用慢很多,這其中設(shè)計到clr中內(nèi)部的處理,不做深究。然而,我們在某些情況下又無法不使用反射,比如:在一個orm框架中,你要將一個datarow轉(zhuǎn)化為一個對象,但你又不清楚該對象有什么屬性,這時候你就需要寫一個通用的泛型方法來處理,以下代碼寫得有點惡心,但不妨礙理解意思:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
//將datareader轉(zhuǎn)化為一個對象 private static t getobj<t>(sqlitedatareader reader) where t : class { t obj = new t(); propertyinfo[] pros = obj.gettype().getproperties(); foreach (propertyinfo item in pros) { try { int32 index = reader.getordinal(item.name); string result = reader.getstring(index); if ( typeof ( string ) == item.propertytype) { item.setvalue(obj, result); continue ; } if ( typeof (datetime) == item.propertytype) { item.setvalue(obj, convert.todatetime(result)); continue ; } if ( typeof (boolean) == item.propertytype) { item.setvalue(obj, convert.toboolean(result)); continue ; } if ( typeof (int32) == item.propertytype) { item.setvalue(obj, convert.toint32(result)); continue ; } if ( typeof (single) == item.propertytype) { item.setvalue(obj, convert.tosingle(result)); continue ; } if ( typeof (single) == item.propertytype) { item.setvalue(obj, convert.tosingle(result)); continue ; } if ( typeof ( double ) == item.propertytype) { item.setvalue(obj, convert.todouble(result)); continue ; } if ( typeof ( decimal ) == item.propertytype) { item.setvalue(obj, convert.todecimal(result)); continue ; } if ( typeof ( byte ) == item.propertytype) { item.setvalue(obj, convert.tobyte(result)); continue ; } } catch (argumentoutofrangeexception ex) { continue ; } } return obj; } |
對于這種情況,其執(zhí)行效率是特別低下的,具體多慢在下面例子會在.net core平臺上和.net framework4.0運(yùn)行測試案例.對于以上我舉例的情況,效率上我們還可以得到提升。但對于想在運(yùn)行時修改一下屬性的名稱或其他操作,反射還是一項特別的神器,因此在某些情況下反射還是無法避免的。
但是對于只是簡單的setvalue或者getvalue,包括用反射構(gòu)造函數(shù),我們可以想一個中繼的方法,那就是使用表達(dá)式樹。對于不理解表達(dá)式樹的,可以到微軟文檔查看,。表達(dá)式樹很容易通過對象模型表示表達(dá)式,因此強(qiáng)烈建議學(xué)習(xí)。查看以下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
static void main() { dog dog = new dog(); propertyinfo propertyinfo = dog.gettype().getproperty(nameof(dog.name)); //獲取對象dog的屬性 methodinfo settermethodinfo = propertyinfo.getsetmethod(); //獲取屬性name的set方法 parameterexpression param = expression.parameter( typeof (dog), "param" ); expression getpropertyvalueexp = expression.lambda(expression.property(param, nameof(dog.name)), param); expression<func<dog, string >> getpropertyvaluelambda = (expression<func<dog, string >>)getpropertyvalueexp; parameterexpression paramo = expression.parameter( typeof (dog), "param" ); parameterexpression parami = expression.parameter( typeof ( string ), "newvalue" ); methodcallexpression methodcallsetterofproperty = expression.call(paramo, settermethodinfo, parami); expression setpropertyvalueexp = expression.lambda(methodcallsetterofproperty, paramo, parami); expression<action<dog, string >> setpropertyvaluelambda = (expression<action<dog, string >>)setpropertyvalueexp; //創(chuàng)建了屬性name的get方法表達(dá)式和set方法表達(dá)式,當(dāng)然只是最簡單的 func<dog, string > getter = getpropertyvaluelambda.compile(); action<dog, string > setter = setpropertyvaluelambda.compile(); setter?.invoke(dog, "wlj" ); //我們現(xiàn)在對dog這個對象的name屬性賦值 string dogname = getter?.invoke(dog); //獲取屬性name的值 console.writeline(dogname); console.readkey(); } public class dog { public string name { get ; set ; } } |
以下代碼可能很難看得懂,但只要知道我們創(chuàng)建了屬性的get、set這兩個方法就行,其結(jié)果最后也能輸出狗的名字 wlj,擁有expressiontree的好處是他有一個名為compile()的方法,它創(chuàng)建一個代表表達(dá)式的代碼塊。現(xiàn)在是最有趣的部分,假設(shè)你在編譯時不知道類型(在這篇文章中包含的代碼我在不同的程序集上創(chuàng)建了一個類型)你仍然可以應(yīng)用這種技術(shù),我將對于常用的屬性的set,get操作進(jìn)行分裝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
/// <summary> /// 屬性類,仿造反射中的propertyinfo /// </summary> public class property { private readonly propertygetter getter; private readonly propertysetter setter; public string name { get ; private set ; } public propertyinfo info { get ; private set ; } public property(propertyinfo propertyinfo) { if (propertyinfo == null ) throw new nullreferenceexception( "屬性不能為空" ); this .name = propertyinfo.name; this .info = propertyinfo; if ( this .info.canread) { this .getter = new propertygetter(propertyinfo); } if ( this .info.canwrite) { this .setter = new propertysetter(propertyinfo); } } /// <summary> /// 獲取對象的值 /// </summary> /// <param name="instance"></param> /// <returns></returns> public object getvalue( object instance) { return getter?.invoke(instance); } /// <summary> /// 賦值操作 /// </summary> /// <param name="instance"></param> /// <param name="value"></param> public void setvalue( object instance, object value) { this .setter?.invoke(instance, value); } private static readonly concurrentdictionary<type, core.reflection.property[]> securitycache = new concurrentdictionary<type, property[]>(); public static core.reflection.property[] getproperties(type type) { return securitycache.getoradd(type, t => t.getproperties().select(p => new property(p)).toarray()); } } /// <summary> /// 屬性get操作類 /// </summary> public class propertygetter { private readonly func< object , object > funcget; public propertygetter(propertyinfo propertyinfo) : this (propertyinfo?.declaringtype, propertyinfo.name) { } public propertygetter(type declaretype, string propertyname) { if (declaretype == null ) { throw new argumentnullexception(nameof(declaretype)); } if (propertyname == null ) { throw new argumentnullexception(nameof(propertyname)); } this .funcget = creategetvaluedeleagte(declaretype, propertyname); } //代碼核心部分 private static func< object , object > creategetvaluedeleagte(type declaretype, string propertyname) { // (object instance) => (object)((declaringtype)instance).propertyname var param_instance = expression.parameter( typeof ( object )); var body_objtotype = expression.convert(param_instance, declaretype); var body_gettypeproperty = expression.property(body_objtotype, propertyname); var body_return = expression.convert(body_gettypeproperty, typeof ( object )); return expression.lambda<func< object , object >>(body_return, param_instance).compile(); } public object invoke( object instance) { return this .funcget?.invoke(instance); } } public class propertysetter { private readonly action< object , object > setfunc; public propertysetter(propertyinfo property) { if (property == null ) { throw new argumentnullexception(nameof(property)); } this .setfunc = createsetvaluedelagate(property); } private static action< object , object > createsetvaluedelagate(propertyinfo property) { // (object instance, object value) => // ((instancetype)instance).set_xxx((propertytype)value) //聲明方法需要的參數(shù) var param_instance = expression.parameter( typeof ( object )); var param_value = expression.parameter( typeof ( object )); var body_instance = expression.convert(param_instance, property.declaringtype); var body_value = expression.convert(param_value, property.propertytype); var body_call = expression.call(body_instance, property.getsetmethod(), body_value); return expression.lambda<action< object , object >>(body_call, param_instance, param_value).compile(); } public void invoke( object instance, object value) { this .setfunc?.invoke(instance, value); } } |
在將代碼應(yīng)用到實例:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
dog dog = new dog(); propertyinfo propertyinfo = dog.gettype().getproperty(nameof(dog.name)); //反射操作 propertyinfo.setvalue(dog, "wlj" ); string result = propertyinfo.getvalue(dog) as string ; console.writeline(result); //表達(dá)式樹的操作 property property = new property(propertyinfo); property.setvalue(dog, "wlj2" ); string result2 = propertyinfo.getvalue(dog) as string ; console.writeline(result2); |
發(fā)現(xiàn)其實現(xiàn)的目的與反射一致,但效率卻有明顯的提高。
以下測試以下他們兩之間的效率。測試代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
student student = new student(); propertyinfo propertyinfo = student.gettype().getproperty(nameof(student.name)); property expproperty = new property(propertyinfo); int32 loopcount = 1000000; codetimer.initialize(); //測試環(huán)境初始化 //下面該方法個執(zhí)行1000000次 codetimer.time( "基礎(chǔ)反射" , loopcount, () => { propertyinfo.setvalue(student, "fode" , null ); }); codetimer.time( "lambda表達(dá)式樹" , loopcount, () => { expproperty.setvalue(student, "fode" ); }); codetimer.time( "直接賦值" , loopcount, () => { student.name = "fode" ; }); console.readkey(); |
其.net4.0環(huán)境下運(yùn)行結(jié)果如下:
.net core環(huán)境下運(yùn)行結(jié)果:
從以上結(jié)果可以知道,迭代同樣的次數(shù)反射需要183ms,而用表達(dá)式只要34ms,直接賦值需要7ms,在效率上,使用表達(dá)式這種方法有顯著的提高,您可以看到使用此技術(shù)可以完全避免使用反射時的性能損失。反射之所以效率有點低主要取決于其加載的時候時在運(yùn)行期下,而表達(dá)式則在編譯期,下篇有空將會介紹用emit技術(shù)優(yōu)化反射,會比表達(dá)式略快一點。
注:對于常用對象的屬性,最好將其緩存起來,這樣效率會更高。。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:https://www.cnblogs.com/fode/p/10079630.html