在動(dòng)態(tài)編程時(shí),我們常常需要運行時(shí)確定調用對象的哪個(gè)屬性或哪個(gè)方法。這個(gè)任務(wù)通??梢杂梅瓷鋪?lái)解決。但眾所周知,反射的性能要比靜態(tài)指定的方式低很多,因為反射要通過(guò)運行時(shí)復雜的機制完成。能否獲得性能和靈活性兼備的動(dòng)態(tài)調用?我在開(kāi)發(fā)VBF的最新功能時(shí)反復考慮了這個(gè)問(wèn)題。我們通常動(dòng)態(tài)調用一個(gè)對象的屬性是采用這樣的手法,假設對象a有一個(gè)屬性叫做MyProp:
Type t = a.GetType();
PropertyInfo pi = t.GetProperty("MyProp");
string value = (string)pi.GetValue(a, null);
注意到什么問(wèn)題了嗎?我們知道這個(gè)屬性的類(lèi)型是string,也知道它沒(méi)有參數。當然也有不知道即將調用的屬性類(lèi)型及參數的時(shí)候,但這個(gè)場(chǎng)合我們知道,卻沒(méi)有利用,還是當成什么信息都不知道一樣使用純動(dòng)態(tài)的手法獲取。這樣我們就錯失了能利用強類(lèi)型特性加速這一過(guò)程的良機。同樣還有方法調用,我們有時(shí)候只是方法或屬性的名字在編譯時(shí)不知道(比如需要用戶(hù)指定),但方法或屬性的類(lèi)型及簽名我們是知道的,這種情況下就可以用泛型和委托技術(shù)高性能地調用。
泛型技術(shù)為處理類(lèi)型提供了方便,除此之外,.NET的委托還具有一些額外的良好特性。委托可以擔當類(lèi)似接口的任務(wù),但與接口最大的不同就在于,方法無(wú)須聲明自己滿(mǎn)足某個(gè)委托,而只要簽名符合,即可賦給委托變量。這樣我們就可以利用一組事先聲明的委托,處理千變萬(wàn)化類(lèi)型的屬性與方法。
C#不允許屬性帶有參數,除非是索引器。VB允許屬性帶有參數但很少有人真的大量使用。于是在真實(shí)世界中屬性的getter和setter的形式就被限定了,絕大部分屬性的getter和setter可以用以下兩個(gè)委托表示:
public delegate void PropertySetter<T>(T value);
public delegate T PropertyGetter<T>();
有了這兩個(gè)委托,我們就可以對已知類(lèi)型但名字需要動(dòng)態(tài)化的屬性進(jìn)行高速的強類(lèi)型動(dòng)態(tài)訪(fǎng)問(wèn)了。方法是使用反射獲取屬性Gettet或Setter的MethodInfo,再使用MethodInfo創(chuàng )建委托:
Type t = a.GetType();
PropertyInfo pi = t.GetProperty("MyProp");
MethodInfo getter = pi.GetGetMethod();
PropertyGetter<string> strPropGetter =
(PropertyGetter<string>)Delegate.CreateDelegate(
typeof(PropertyGetter<string>), a, getter);
string value = strPropGetter();
注意,這個(gè)方法在調用前進(jìn)行了更多反射操作,因此,如果你只想一兩次地獲取屬性的值,這種方法還不如直接用放射來(lái)的快。但是,當你需要對同一屬性進(jìn)行成千上萬(wàn)次訪(fǎng)問(wèn)時(shí),絕對值得多寫(xiě)這點(diǎn)代碼,在string類(lèi)型的簡(jiǎn)單屬性上,速度可比直接反射獲取最多快達1000倍,這是我實(shí)測的結果。
接下來(lái)我們討論有index的屬性和方法的調用。C#盡允許在索引器的語(yǔ)法上使用屬性參數,而在VB看來(lái),索引器不過(guò)是類(lèi)所有帶參數的屬性中比較特殊的一個(gè),他得到了在對象上使用數組語(yǔ)法訪(fǎng)問(wèn)的特權。不管怎么說(shuō),無(wú)論是索引器還是普通帶參數的屬性,他們的getter和setter過(guò)程都不像典型屬性那樣簡(jiǎn)單。同樣還有對方法的調用,方法的簽名千變萬(wàn)化,似乎我們很難用預先定義的委托統一進(jìn)行調用。事實(shí)的確如此,不過(guò)與針對每一種屬性訪(fǎng)問(wèn)器或方法的簽名定義一種委托的做法相比,泛型還是給出了一種稍微舒服一點(diǎn)的做法:
public delegate R Func<R>();
public delegate R Func<T0, R>(T0 a0);
public delegate R Func<T0, T1, R>(T0 a0, T1 a1);
public delegate R Func<T0, T1, T2, R>(T0 a0, T1 a1, T2 a2);
這樣一組泛型委托,可以涵蓋參數數目從0-3,有返回值并且沒(méi)有參數是out或ref的所有方法簽名。你還可以定義一組用于無(wú)返回值的。有了這樣一組泛型委托,就可以在想要某種函數的簽名時(shí)直接創(chuàng )建出來(lái),而無(wú)須聲明新的類(lèi)型。再結合剛才的手法,就可以用統一的手法實(shí)現大部分帶有參數的屬性或方法的動(dòng)態(tài)調用——同時(shí)獲得動(dòng)態(tài)名稱(chēng)和強類(lèi)型性能的雙重好處。
也許你早已經(jīng)利用了類(lèi)似的手法,并用于除了動(dòng)態(tài)調用屬性或方法以外的其他任務(wù)。我只是在開(kāi)發(fā)VBF時(shí)想到了他們,希望能對部分需要的人有所幫助。
聯(lián)系客服