深入理解C# 3.x的新特性(2):Extension Method[下篇]
四、Extension Method的本質(zhì)
通過(guò)上面一節(jié)的介紹,我們知道了在C#中如何去定義一個(gè)Extension Method:它是定義在一個(gè)Static class中的、第一個(gè)Parameter標(biāo)記為this關(guān)鍵字的Static Method。在這一節(jié)中,我們來(lái)進(jìn)一步認(rèn)識(shí)Extension Method。
和C# 3.0的其他新特性相似,Extension Method僅僅是C#這種.NET Programming Language的新特性而已。我們知道,C#是一種典型的編譯型的語(yǔ)言,我們編寫的Source Code必須先經(jīng)過(guò)和C# Compiler編譯成Assembly,才能被CLR加載,被JIT 編譯成Machine Instruction并最終被執(zhí)行。C# 3.0的這些新的特性大都影響Source被C# Compiler編譯成Assembly這個(gè)階段,換句話說(shuō),這些新特僅僅是Compiler的新特性而已。通過(guò)對(duì)Compiler進(jìn)行修正,促使他將C# 3.0引入的新的語(yǔ)法編譯成相對(duì)應(yīng)的IL Code,從本質(zhì)上看,這些IL Code 和原來(lái)的IL并沒(méi)有本質(zhì)的區(qū)別。所有當(dāng)被編譯生成成Assembly被CLR加載、執(zhí)行的時(shí)候,CLR是意識(shí)不到這些新的特性的。
從Extension Method的定義我們可看出,Extension Method本質(zhì)上是一個(gè)Static Method。但是我們往往以Instance Method的方式進(jìn)行調(diào)用。C# Compiler的作用很明顯:把一個(gè)以Instance Method方式調(diào)用的Source Code編譯成的于對(duì)應(yīng)于傳統(tǒng)的Static Method調(diào)用的IL Code。
雖然Extension Method本質(zhì)上僅僅是一個(gè)Static Class的Static Method成員,但是畢竟和傳統(tǒng)的Static Method有所不同:在第一個(gè)Parameter前加了一個(gè)this關(guān)鍵字。我們現(xiàn)在來(lái)看看他們之間的細(xì)微的差異。我們先定義一個(gè)一般的Static Method:
public?static?Vector?Adds(Vector?v,?Vector?v1){
??return?new?Vector?{?X?=?v.X?+?v1.X,?Y?=?v.Y?+?v1.Y?};
}
注:Vector的定義參見(jiàn)《深入理解C# 3.0的新特性(2):Extension Method - Part I》。
我們來(lái)看看通過(guò)Compiler進(jìn)行編譯生成的IL:
.method?private?hidebysig?static?void??Main(string[]?args)?cil?managed{
??.entrypoint
??//?Code?size???????50?(0x32)
??.maxstack??2
??.locals?init?([0]?class?Artech.ExtensionMethod.Vector?v,
???????????[1]?class?Artech.ExtensionMethod.Vector?'<>g__initLocal0')
??IL_0000:??nop
??IL_0001:??newobj?????instance?void?Artech.ExtensionMethod.Vector::.ctor()
??IL_0006:??stloc.1
??IL_0007:??ldloc.1
??IL_0008:??ldc.r8?????1.
??IL_0011:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_X(float64)
??IL_0016:??nop
??IL_0017:??ldloc.1
??IL_0018:??ldc.r8?????2.
??IL_0021:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_Y(float64)
??IL_0026:??nop
??IL_0027:??ldloc.1
??IL_0028:??stloc.0
??IL_0029:??ldloc.0
??IL_002a:??ldloc.0
??IL_002b:??call???????class?Artech.ExtensionMethod.Vector?Artech.ExtensionMethod.Extension::Adds(class?Artech.ExtensionMethod.Vector,
class?Artech.ExtensionMethod.Vector)
??IL_0030:??stloc.0
??IL_0031:??ret
}?//?end?of?method?Program::Main
對(duì)了解IL的人來(lái)說(shuō),對(duì)上面的IL code應(yīng)該很容易理解。
我們?cè)賮?lái)看看對(duì)于通過(guò)下面的方式定義的Extension Method:
public?static?class?Extension????{
?????????public?static?Vector?Adds(this?Vector?v,?Vector?v1)
????????{
????????????return?new?Vector?{?X?=?v.X?+?v1.X,?Y?=?v.Y?+?v1.Y?};
????????}
}
對(duì)于得IL如下:
.method?public?hidebysig?static?class?Artech.ExtensionMethod.Vector?Adds(class?Artech.ExtensionMethod.Vector?v,
class?Artech.ExtensionMethod.Vector?v1)?cil?managed
{
??.custom?instance?void?[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()?=?(?01?00?00?00?)?
??//?Code?size???????53?(0x35)
??.maxstack??3
??.locals?init?([0]?class?Artech.ExtensionMethod.Vector?'<>g__initLocal0',
???????????[1]?class?Artech.ExtensionMethod.Vector?CS$1$0000)
??IL_0000:??nop
??IL_0001:??newobj?????instance?void?Artech.ExtensionMethod.Vector::.ctor()
??IL_0006:??stloc.0
??IL_0007:??ldloc.0
??IL_0008:??ldarg.0
??IL_0009:??callvirt???instance?float64?Artech.ExtensionMethod.Vector::get_X()
??IL_000e:??ldarg.1
??IL_000f:??callvirt???instance?float64?Artech.ExtensionMethod.Vector::get_X()
??IL_0014:??add
??IL_0015:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_X(float64)
??IL_001a:??nop
??IL_001b:??ldloc.0
??IL_001c:??ldarg.0
??IL_001d:??callvirt???instance?float64?Artech.ExtensionMethod.Vector::get_Y()
??IL_0022:??ldarg.1
??IL_0023:??callvirt???instance?float64?Artech.ExtensionMethod.Vector::get_Y()
??IL_0028:??add
??IL_0029:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_Y(float64)
??IL_002e:??nop
??IL_002f:??ldloc.0
??IL_0030:??stloc.1
??IL_0031:??br.s???????IL_0033
??IL_0033:??ldloc.1
??IL_0034:??ret
}?//?end?of?method?Extension::Adds
通過(guò)比較,我們發(fā)現(xiàn)和上面定義的一般的Static Method生成的IL唯一的區(qū)別就是:在Adds方法定義最開(kāi)始添加了下面一段代碼:
.custom?instance?void?[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()?=?(?01?00?00?00?)?這段添加的IL代碼很明顯,就是在Adds方法上添加一個(gè)Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的定義:
[AttributeUsage(AttributeTargets.Method?|?AttributeTargets.Class?|?AttributeTargets.Assembly)]public?sealed?class?ExtensionAttribute?:?Attribute
{
}
所以下面Extension Method的定義
public?static?Vector?Adds(this?Vector?v,?Vector?v1){
????????????return?new?Vector?{?X?=?v.X?+?v1.X,?Y?=?v.Y?+?v1.Y?};
}
和下面的定義是等效的
[ExtensionAttribute]public?static?Vector?Adds(Vector?v,?Vector?v1)?
{
????????????return?new?Vector?{?X?=?v.X?+?v1.X,?Y?=?v.Y?+?v1.Y?};
}
但是,System.Runtime.CompilerServices.ExtensionAttribute和其他Custom Attribute不一樣,因?yàn)樗菫榱薊xtension Method的而定義的,我們只能通過(guò)添加this Key word的語(yǔ)法來(lái)定義Extension Method。所以當(dāng)我們將System.Runtime.CompilerServices.ExtensionAttribute直接運(yùn)用到Adds方法會(huì)出現(xiàn)下面的Compile Error:
Do?not?use?'System.Runtime.CompilerServices.ExtensionAttribute'.?Use?the?'this'?keyword?instead.上面我們比較了Extension Method本身IL和一般Static Method IL,現(xiàn)在我們看看當(dāng)我們以Instance Method方式調(diào)用Extension Method的IL。假設(shè)我們通過(guò)下面的方式調(diào)用Adds。
class?Program????{
????????static?void?Main(string[]?args)
????????{
?var?v?=?new?Vector?{?X?=?1,?Y?=?2?};
???????????v?=?v.Adds(v);
????????}
}
下面是Main Method的IL:
.method?private?hidebysig?static?void??Main(string[]?args)?cil?managed{
??.entrypoint
??//?Code?size???????50?(0x32)
??.maxstack??2
??.locals?init?([0]?class?Artech.ExtensionMethod.Vector?v,
???????????[1]?class?Artech.ExtensionMethod.Vector?'<>g__initLocal0')
??IL_0000:??nop
??IL_0001:??newobj?????instance?void?Artech.ExtensionMethod.Vector::.ctor()
??IL_0006:??stloc.1
??IL_0007:??ldloc.1
??IL_0008:??ldc.r8?????1.
??IL_0011:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_X(float64)
??IL_0016:??nop
??IL_0017:??ldloc.1
??IL_0018:??ldc.r8?????2.
??IL_0021:??callvirt???instance?void?Artech.ExtensionMethod.Vector::set_Y(float64)
??IL_0026:??nop
??IL_0027:??ldloc.1
??IL_0028:??stloc.0
??IL_0029:??ldloc.0
??IL_002a:??ldloc.0
??IL_002b:??call???????class?Artech.ExtensionMethod.Vector?Artech.ExtensionMethod.Extension::Adds(class?Artech.ExtensionMethod.Vector,
class?Artech.ExtensionMethod.Vector)
??IL_0030:??stloc.0
??IL_0031:??ret
}?//?end?of?method?Program::Main
通過(guò)上面的IL,我們看到調(diào)用的是Artech.ExtensionMethod.Extension的Adds方法。
IL_002b:??call?class?Artech.ExtensionMethod.Vector?Artech.ExtensionMethod.Extension::Adds(class?Artech.ExtensionMethod.Vector,class?Artech.ExtensionMethod.Vector)
通過(guò)對(duì)IL的分析,我們基本上看出了Extension Method的本質(zhì)。我們?cè)賮?lái)簡(jiǎn)單描述一下對(duì)Compiler的編譯過(guò)程:當(dāng)Compiler對(duì)Adds方法的調(diào)用進(jìn)行編譯的過(guò)程的時(shí)候,它必須判斷這個(gè)Adds方式是Vector Type的成員還是以Extension Method的方式定義。Extension Method的優(yōu)先級(jí)是最低的,只有確定Vector中沒(méi)有定義相應(yīng)的Adds方法的時(shí)候,Compiler才會(huì)在引用的Namespace中查看這些Namespace中是否定義有對(duì)應(yīng)的Adds Extension Method的Static Class。找到后作進(jìn)行相應(yīng)的編譯,否則出現(xiàn)編譯錯(cuò)誤。
五、一個(gè)完整的Extension Method的Sample
在介紹了Extension Method的本質(zhì)之后,我們通過(guò)一個(gè)相對(duì)完整的Sample進(jìn)一步了解Extension Method的運(yùn)用,通過(guò)這個(gè)Sample,我們還可以粗略了解LINQ的原理。
C# 3.0為L(zhǎng)INQ定義了一系列的Operator:select, from,where,orderby..., 促使我們按照OO的方式來(lái)處理各種各樣的數(shù)據(jù),比如XML,Relational DB Data,C#中IEnumeratable<T> Object。比如:
var?names?=?new?List<string>?{?"Tom?Cruise",?"Tom?Hanks",?"Al?Pacino",?"Harrison?Ford"?};var?result?=?names.Where(name?=>?name.StartsWith("Tom"));
foreach(var?name?in?result)
{
??????Console.WriteLine(name);
}
我們通過(guò)上面的Code,從一系列的姓名列表中("Tom Cruise", "Tom Hanks", "Al Pacino", "Harrison Ford")篩選名字(First Name)為Tom的姓名。通過(guò)Where Operator,傳入一個(gè)以Lambda Expression表示的篩選條件(name => name.StartsWith("Tom"))。Where Operator就是通過(guò)Extension Method的方式定義的。
在這里提供的Sample就是定義一個(gè)完成Where Operator相同功能的Operator,我們把這個(gè)Operator起名為When。
using?System;using?System.Collections.Generic;
using?System.Linq;
using?System.Text;
using?System.Collections;
namespace?Artech.ExtensionMethod
{
????public?delegate?TResult?Function<Tparam,?TResult>(Tparam?param);
????public?static?class?Extension
????{
????????public?static?IEnumerable<TSource>?When<TSource>(this?IEnumerable<TSource>?source,?Function<TSource,?bool>?predicate)
????????{
????????????return?new?WhenEnumerator<TSource>(source,?predicate);
????????}??
????}
????public?class?WhenEnumerator<TSource>?:?IEnumerable<TSource>,?IEnumerator<TSource>
????{
????????private?IEnumerable<TSource>?_source;
????????private?Function<TSource,?bool>?_predicate;
????????private?IEnumerator<TSource>?_sourceEnumerator;
????????public?WhenEnumerator(IEnumerable<TSource>?source,?Function<TSource,?bool>?predicate)
????????{
????????????this._source?=?source;
????????????this._predicate?=?predicate;
????????????this._sourceEnumerator?=?this._source.GetEnumerator();
????????}
????????IEnumerable?Members#region?IEnumerable<TSource>?Members
????????public?IEnumerator<TSource>?GetEnumerator()
????????{
????????????return?new?WhenEnumerator<TSource>(this._source,?this._predicate);
????????}
????????#endregion
????????IEnumerable?Members#region?IEnumerable?Members
????????IEnumerator?IEnumerable.GetEnumerator()
????????{
????????????throw?new?Exception("The?method?or?operation?is?not?implemented.");
????????}
????????#endregion
????????IEnumerator?Members#region?IEnumerator<TSource>?Members
????????public?TSource?Current
????????{
????????????get?{?return?this._sourceEnumerator.Current;?}
????????}
????????#endregion
????????IDisposable?Members#region?IDisposable?Members
????????public?void?Dispose()
????????{
????????????//throw?new?Exception("The?method?or?operation?is?not?implemented.");
????????}
????????#endregion
????????IEnumerator?Members#region?IEnumerator?Members
????????object?IEnumerator.Current
????????{
????????????get
????????????{
????????????????return?this._sourceEnumerator.Current;
????????????}
????????}
????????public?bool?MoveNext()
????????{
????????????if?(!this._sourceEnumerator.MoveNext())
????????????{
????????????????return?false;
????????????}
????????????while?(!this._predicate(this._sourceEnumerator.Current))
????????????{
????????????????if?(!this._sourceEnumerator.MoveNext())
????????????????{
????????????????????return?false;
????????????????}
????????????}
????????????return?true;
????????}
????????public?void?Reset()
????????{
????????????this._sourceEnumerator.Reset();
????????}
????????#endregion
????}
}
我們來(lái)看看我們新的LINQ Operator:When的定義。我首先定義了一個(gè)Generic Delegate:Function。實(shí)際上他定義了一個(gè)一元函數(shù)y?=?f(x),TParam和TResult為參數(shù)和返回值得類型。?
public?delegate?TResult?Function<Tparam,?TResult>(Tparam?param);接著在Static Class Extesnion中定義了Extension Method:When。該方法包含兩個(gè)參數(shù),其中一個(gè)是執(zhí)行篩選的數(shù)據(jù)源,另一個(gè)是用于判斷數(shù)據(jù)源每個(gè)對(duì)象是否滿足你所定義的篩選條件的斷言。返回一個(gè)我們自定義的、實(shí)現(xiàn)了IEnumerable的WhenEnumerator對(duì)象。
public?static?class?Extension????{
????????public?static?IEnumerable<TSource>?When<TSource>(this?IEnumerable<TSource>?source,?Function<TSource,?bool>?predicate)
????????{
????????????return?new?WhenEnumerator<TSource>(source,?predicate);
????????}?
????}
WhenEnumerator的定義是實(shí)現(xiàn)When Extension Method的關(guān)鍵,我們現(xiàn)在著重來(lái)介紹它的具體實(shí)現(xiàn)。WhenEnumerator實(shí)現(xiàn)了Interface Enumerable<T>,為了簡(jiǎn)單,我們也它對(duì)應(yīng)的Enumerator的實(shí)現(xiàn)也定義在同一個(gè)Class中,所以WhenEnumerator實(shí)現(xiàn)了兩個(gè)Interface:IEnumerable<TSource>, IEnumerator<TSource>。?
以下3個(gè)成員分別代表:用于執(zhí)行篩選的數(shù)據(jù)源、用于判斷是否滿足篩選條件的斷言以及數(shù)據(jù)源的Enumerator對(duì)象。
private?IEnumerable<TSource>?_source;private?Function<TSource,?bool>?_predicate;
private?IEnumerator<TSource>?_sourceEnumerator;
通過(guò)返回一個(gè)WhenEnumerator對(duì)象,實(shí)現(xiàn)了IEnumerable<TSource>的GetEnumerator()方法。?
????????public?IEnumerator<TSource>?GetEnumerator()????????{
????????????return?new?WhenEnumerator<TSource>(this._source,?this._predicate);
????????}
對(duì)于另一個(gè)Interface IEnumerator<TSource>,直接調(diào)用數(shù)據(jù)源的Enumerator的同名方法實(shí)現(xiàn)了Current,和Reset()。對(duì)于MoveNext()則通過(guò)如下的方式實(shí)現(xiàn):把當(dāng)前的位置設(shè)置在下一個(gè)滿足篩選條件的Element上。
public?bool?MoveNext()????????{
????????????if?(!this._sourceEnumerator.MoveNext())
????????????{
????????????????return?false;
????????????}
????????????while?(!this._predicate(this._sourceEnumerator.Current))
????????????{
????????????????if?(!this._sourceEnumerator.MoveNext())
????????????????{
????????????????????return?false;
????????????????}
????????????}
????????????return?true;
????????}
到現(xiàn)在為止,這個(gè)新的LINQ Operator被創(chuàng)建,現(xiàn)在我們可以按照使用Where operator的方式來(lái)調(diào)用When。
我們可以通過(guò)Delegate的方式來(lái)使用When Operator:
class?Program????{
????????static?void?Main()
????????{
????????????var?names?=?new?List<string>?{?"Tom?Cruise",?"Tom?Hanks",?"Al?Pacino",?"Harrison?Ford"?};
????????????var?result?=?names.When(delegate(string?name)?{?return?name.StartsWith("Tom");?});
????????????foreach?(var?name?in?result)
????????????{
????????????????Console.WriteLine(name);
????????????}
????????}
}
輸出結(jié)果:
Tom?CruiseTom?Hanks
我們也可以通過(guò)Lambda Expression的方式來(lái)使用When Operator:
static?void?Main()????????{
????????????var?names?=?new?List<string>?{?"Tom?Cruise",?"Tom?Hanks",?"Al?Pacino",?"Harrison?Ford"?};
????????????var?result?=?names.When(name=>name.StartsWith("Tom"));
????????????foreach?(var?name?in?result)
????????????{
????????????????Console.WriteLine(name);
????????????}
????????}
顯然這種方式更簡(jiǎn)潔。?
Deferred Evaluation
對(duì)于LINQ,有一個(gè)非常重要的特征:Deferred Evaluation。在了解這個(gè)特征之前,我們來(lái)看一個(gè)例子:
static?void?Main(){
????????????var?names?=?new?List<string>?{?"Tom?Cruise",?"Tom?Hanks",?"Al?Pacino",?"Harrison?Ford"?};
????????????var?result1?=?names.When(name=>name.StartsWith("Tom"));
????????????names[0]?=?"Stephen?Chou";
????????????var?result2?=?names.When(name?=>?name.StartsWith("Tom"));
????????????foreach?(var?name?in?result1)
????????????{
????????????????Console.WriteLine(name);
????????????}
????????????foreach?(var?name?in?result2)
????????????{
????????????????Console.WriteLine(name);
????????????}
}
運(yùn)行程序,你會(huì)發(fā)現(xiàn)兩個(gè)foreach loop顯示的結(jié)果都是一樣的:Tom Hanks。為什么result1實(shí)在第一個(gè)Element被改動(dòng)之前返回的,但我們最終輸出的結(jié)果卻反映的是改動(dòng)之后的數(shù)據(jù)源。通過(guò)我們上面的定義,你很容易得到答案。在這里我要說(shuō)的是LINQ的一個(gè)重要的特性Deferred Evaluation:在調(diào)用Operator的時(shí)候并不會(huì)有任何的任何數(shù)據(jù)獲取的過(guò)程,這個(gè)階段的任務(wù)是創(chuàng)建一個(gè)同于獲取數(shù)據(jù)的表達(dá)式。只要你真正所用到這個(gè)數(shù)據(jù)的時(shí)候,采用重?cái)?shù)據(jù)源中通過(guò)你構(gòu)建的表達(dá)式通過(guò)查詢獲取數(shù)據(jù)。
C# 3.x相關(guān)內(nèi)容:
[原創(chuàng)]深入理解C# 3.x的新特性(1):Anonymous Type
[原創(chuàng)]深入理解C# 3.x的新特性(2):Extension Method - Part I
[原創(chuàng)]深入理解C# 3.x的新特性(2):Extension Method - Part II
[原創(chuàng)]深入理解C# 3.x的新特性(3):從Delegate、Anonymous Method到Lambda Expression
[原創(chuàng)]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原創(chuàng)]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer
轉(zhuǎn)載于:https://www.cnblogs.com/artech/archive/2007/07/19/823847.html
總結(jié)
以上是生活随笔為你收集整理的深入理解C# 3.x的新特性(2):Extension Method[下篇]的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 4.8网络层设备
- 下一篇: Unity3D-C#脚本介绍