四、Extension Method的本质
通过上面一节的介绍,我们知道了在C#中如何去定义一个Extension Method:它是定义在一个Static class中的、第一个Parameter标记为this关键字的Static Method。在这一节中,我们来进一步认识Extension Method。
和C# 3.0的其他新特性相似,Extension Method仅仅是C#这种.NET Programming Language的新特性而已。我们知道,C#是一种典型的编译型的语言,我们编写的Source Code必须先经过和C# Compiler编译成Assembly,才能被CLR加载,被JIT 编译成Machine Instruction并最终被执行。C# 3.0的这些新的特性大都影响Source被C# Compiler编译成Assembly这个阶段,换句话说,这些新特仅仅是Compiler的新特性而已。通过对Compiler进行修正,促使他将C# 3.0引入的新的语法编译成相对应的IL Code,从本质上看,这些IL Code 和原来的IL并没有本质的区别。所有当被编译生成成Assembly被CLR加载、执行的时候,CLR是意识不到这些新的特性的。
从Extension Method的定义我们可看出,Extension Method本质上是一个Static Method。但是我们往往以Instance Method的方式进行调用。C# Compiler的作用很明显:把一个以Instance Method方式调用的Source Code编译成的于对应于传统的Static Method调用的IL Code。
虽然Extension Method本质上仅仅是一个Static Class的Static Method成员,但是毕竟和传统的Static Method有所不同:在第一个Parameter前加了一个this关键字。我们现在来看看他们之间的细微的差异。我们先定义一个一般的Static Method:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
public
static
VectorAdds(Vectorv,Vectorv1)
{
return
new
Vector{X
=
v.X
+
v1.X,Y
=
v.Y
+
v1.Y};
}
注:Vector的定义参见《深入理解C# 3.0的新特性(2):Extension Method - Part I》。
我们来看看通过Compiler进行编译生成的IL:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
.method
private
hidebysig
static
void
Main(
string
[]args)cilmanaged
{
.entrypoint
//
Codesize50(0x32)
.maxstack
2
.localsinit([
0
]
class
Artech.ExtensionMethod.Vectorv,
[
1
]
class
Artech.ExtensionMethod.Vector
'
<>g__initLocal0
'
)
IL_0000:nop
IL_0001:newobjinstance
void
Artech.ExtensionMethod.Vector::.ctor()
IL_0006:stloc.
1
IL_0007:ldloc.
1
IL_0008:ldc.r8
1
.
IL_0011:callvirtinstance
void
Artech.ExtensionMethod.Vector::set_X(float64)
IL_0016:nop
IL_0017:ldloc.
1
IL_0018:ldc.r8
2
.
IL_0021:callvirtinstance
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.VectorArtech.ExtensionMethod.Extension::Adds(
class
Artech.ExtensionMethod.Vector,
class
Artech.ExtensionMethod.Vector)
IL_0030:stloc.
0
IL_0031:ret
}
//
endofmethodProgram::Main
对了解IL的人来说,对上面的IL code应该很容易理解。
我们再来看看对于通过下面的方式定义的Extension Method:
public
static
class
Extension
{
publicstaticVectorAdds(thisVectorv,Vectorv1)
{
returnnewVector{X=v.X+v1.X,Y=v.Y+v1.Y};
}
}
对于得IL如下:
.method
public
hidebysig
static
class
Artech.ExtensionMethod.Vector
Adds(
class
Artech.ExtensionMethod.Vectorv,
class
Artech.ExtensionMethod.Vectorv1)cilmanaged
{
.custominstancevoid[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()=(01000000)
//Codesize53(0x35)
.maxstack3
.localsinit([0]classArtech.ExtensionMethod.Vector'<>g__initLocal0',
[1]classArtech.ExtensionMethod.VectorCS$1$0000)
IL_0000:nop
IL_0001:newobjinstancevoidArtech.ExtensionMethod.Vector::.ctor()
IL_0006:stloc.0
IL_0007:ldloc.0
IL_0008:ldarg.0
IL_0009:callvirtinstancefloat64Artech.ExtensionMethod.Vector::get_X()
IL_000e:ldarg.1
IL_000f:callvirtinstancefloat64Artech.ExtensionMethod.Vector::get_X()
IL_0014:add
IL_0015:callvirtinstancevoidArtech.ExtensionMethod.Vector::set_X(float64)
IL_001a:nop
IL_001b:ldloc.0
IL_001c:ldarg.0
IL_001d:callvirtinstancefloat64Artech.ExtensionMethod.Vector::get_Y()
IL_0022:ldarg.1
IL_0023:callvirtinstancefloat64Artech.ExtensionMethod.Vector::get_Y()
IL_0028:add
IL_0029:callvirtinstancevoidArtech.ExtensionMethod.Vector::set_Y(float64)
IL_002e:nop
IL_002f:ldloc.0
IL_0030:stloc.1
IL_0031:br.sIL_0033
IL_0033:ldloc.1
IL_0034:ret
}
//
endofmethodExtension::Adds
通过比较,我们发现和上面定义的一般的Static Method生成的IL唯一的区别就是:在Adds方法定义最开始添加了下面一段代码:
.custominstance
void
[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
=
(
01
00
00
00
)
这段添加的IL代码很明显,就是在Adds方法上添加一个Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的定义:
[AttributeUsage(AttributeTargets.Method
|
AttributeTargets.Class
|
AttributeTargets.Assembly)]
public
sealed
class
ExtensionAttribute:Attribute
{
}
所以下面Extension Method的定义
public
static
VectorAdds(
this
Vectorv,Vectorv1)
{
returnnewVector{X=v.X+v1.X,Y=v.Y+v1.Y};
}
和下面的定义是等效的
[ExtensionAttribute]
public
static
VectorAdds(Vectorv,Vectorv1)
{
returnnewVector{X=v.X+v1.X,Y=v.Y+v1.Y};
}
但是,System.Runtime.CompilerServices.ExtensionAttribute和其他Custom Attribute不一样,因为它是为了Extension Method的而定义的,我们只能通过添加this Key word的语法来定义Extension Method。所以当我们将System.Runtime.CompilerServices.ExtensionAttribute直接运用到Adds方法会出现下面的Compile Error:
Donotuse
'
System.Runtime.CompilerServices.ExtensionAttribute
'
.Usethe
'
this
'
keywordinstead.
上面我们比较了Extension Method本身IL和一般Static Method IL,现在我们看看当我们以Instance Method方式调用Extension Method的IL。假设我们通过下面的方式调用Adds。
class
Program
{
staticvoidMain(string[]args)
{
varv=newVector{X=1,Y=2};
v=v.Adds(v);
}
}
下面是Main Method的IL:
.method
private
hidebysig
static
void
Main(
string
[]args)cilmanaged
{
.entrypoint
//Codesize50(0x32)
.maxstack2
.localsinit([0]classArtech.ExtensionMethod.Vectorv,
[1]classArtech.ExtensionMethod.Vector'<>g__initLocal0')
IL_0000:nop
IL_0001:newobjinstancevoidArtech.ExtensionMethod.Vector::.ctor()
IL_0006:stloc.1
IL_0007:ldloc.1
IL_0008:ldc.r81.
IL_0011:callvirtinstancevoidArtech.ExtensionMethod.Vector::set_X(float64)
IL_0016:nop
IL_0017:ldloc.1
IL_0018:ldc.r82.
IL_0021:callvirtinstancevoidArtech.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:callclassArtech.ExtensionMethod.VectorArtech.ExtensionMethod.Extension::Adds(classArtech.ExtensionMethod.Vector,
classArtech.ExtensionMethod.Vector)
IL_0030:stloc.0
IL_0031:ret
}
//
endofmethodProgram::Main
通过上面的IL,我们看到调用的是Artech.ExtensionMethod.Extension的Adds方法。
IL_002b:call
class
Artech.ExtensionMethod.VectorArtech.ExtensionMethod.Extension::Adds(
class
Artech.ExtensionMethod.Vector,
class
Artech.ExtensionMethod.Vector)
通过对IL的分析,我们基本上看出了Extension Method的本质。我们再来简单描述一下对Compiler的编译过程:当Compiler对Adds方法的调用进行编译的过程的时候,它必须判断这个Adds方式是Vector Type的成员还是以Extension Method的方式定义。Extension Method的优先级是最低的,只有确定Vector中没有定义相应的Adds方法的时候,Compiler才会在引用的Namespace中查看这些Namespace中是否定义有对应的Adds Extension Method的Static Class。找到后作进行相应的编译,否则出现编译错误。
五、一个完整的Extension Method的Sample
在介绍了Extension Method的本质之后,我们通过一个相对完整的Sample进一步了解Extension Method的运用,通过这个Sample,我们还可以粗略了解LINQ的原理。
C# 3.0为LINQ定义了一系列的Operator:select, from,where,orderby..., 促使我们按照OO的方式来处理各种各样的数据,比如XML,Relational DB Data,C#中IEnumeratable<T> Object。比如:
varnames
=
new
List
<
string
>
{"TomCruise","TomHanks","AlPacino","HarrisonFord"}
;
varresult
=
names.Where(name
=>
name.StartsWith(
"
Tom
"
));
foreach
(varname
in
result)
{
Console.WriteLine(name);
}
我们通过上面的Code,从一系列的姓名列表中("Tom Cruise", "Tom Hanks", "Al Pacino", "Harrison Ford")筛选名字(First Name)为Tom的姓名。通过Where Operator,传入一个以Lambda Expression表示的筛选条件(name => name.StartsWith("Tom"))。Where Operator就是通过Extension Method的方式定义的。
在这里提供的Sample就是定义一个完成Where Operator相同功能的Operator,我们把这个Operator起名为When。
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Collections;
namespace
Artech.ExtensionMethod
{
publicdelegateTResultFunction<Tparam,TResult>(Tparamparam);
publicstaticclassExtension
{
publicstaticIEnumerable<TSource>When<TSource>(thisIEnumerable<TSource>source,Function<TSource,bool>predicate)
{
returnnewWhenEnumerator<TSource>(source,predicate);
}
}
publicclassWhenEnumerator<TSource>:IEnumerable<TSource>,IEnumerator<TSource>
{
privateIEnumerable<TSource>_source;
privateFunction<TSource,bool>_predicate;
privateIEnumerator<TSource>_sourceEnumerator;
publicWhenEnumerator(IEnumerable<TSource>source,Function<TSource,bool>predicate)
{
this._source=source;
this._predicate=predicate;
this._sourceEnumerator=this._source.GetEnumerator();
}
IEnumerable<tsource>Members</tsource>#regionIEnumerable<TSource>Members
publicIEnumerator<TSource>GetEnumerator()
{
returnnewWhenEnumerator<TSource>(this._source,this._predicate);
}
#endregion
IEnumerableMembers#regionIEnumerableMembers
IEnumeratorIEnumerable.GetEnumerator()
{
thrownewException("Themethodoroperationisnotimplemented.");
}
#endregion
IEnumerator<tsource>Members</tsource>#regionIEnumerator<TSource>Members
publicTSourceCurrent
{
get{returnthis._sourceEnumerator.Current;}
}
#endregion
IDisposableMembers#regionIDisposableMembers
<spa
分享到:
评论