`
varsoft
  • 浏览: 2570421 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II

阅读更多

四、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 /> -->publicstaticVectorAdds(Vectorv,Vectorv1)
{
returnnewVector{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 /> -->.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged
{
.entrypoint
//Codesize50(0x32)
.maxstack2
.localsinit([
0]classArtech.ExtensionMethod.Vectorv,
[
1]classArtech.ExtensionMethod.Vector'<>g__initLocal0')
IL_0000:nop
IL_0001:newobjinstance
voidArtech.ExtensionMethod.Vector::.ctor()
IL_0006:stloc.
1
IL_0007:ldloc.
1
IL_0008:ldc.r8
1.
IL_0011:callvirtinstance
voidArtech.ExtensionMethod.Vector::set_X(float64)
IL_0016:nop
IL_0017:ldloc.
1
IL_0018:ldc.r8
2.
IL_0021:callvirtinstance
voidArtech.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
classArtech.ExtensionMethod.VectorArtech.ExtensionMethod.Extension::Adds(classArtech.ExtensionMethod.Vector,
classArtech.ExtensionMethod.Vector)
IL_0030:stloc.
0
IL_0031:ret
}
//endofmethodProgram::Main

对了解IL的人来说,对上面的IL code应该很容易理解。

我们再来看看对于通过下面的方式定义的Extension Method:

publicstaticclassExtension
{
publicstaticVectorAdds(thisVectorv,Vectorv1)
{
returnnewVector{X=v.X+v1.X,Y=v.Y+v1.Y};
}

}

对于得IL如下:

.methodpublichidebysigstaticclassArtech.ExtensionMethod.Vector
Adds(
classArtech.ExtensionMethod.Vectorv,
classArtech.ExtensionMethod.Vectorv1)cilmanaged
{
.custominstance
void[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:newobjinstance
voidArtech.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:callvirtinstance
voidArtech.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:callvirtinstance
voidArtech.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方法定义最开始添加了下面一段代码:

.custominstancevoid[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()=(01000000)

这段添加的IL代码很明显,就是在Adds方法上添加一个Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的定义:

[AttributeUsage(AttributeTargets.Method|AttributeTargets.Class|AttributeTargets.Assembly)]
publicsealedclassExtensionAttribute:Attribute
{
}

所以下面Extension Method的定义

publicstaticVectorAdds(thisVectorv,Vectorv1)
{
returnnewVector{X=v.X+v1.X,Y=v.Y+v1.Y};
}

和下面的定义是等效的

[ExtensionAttribute]
publicstaticVectorAdds(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。 

classProgram
{
staticvoidMain(string[]args)
{
varv
=newVector{X=1,Y=2};
v
=v.Adds(v);
}

}

下面是Main Method的IL:

.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged
{
.entrypoint
//Codesize50(0x32)
.maxstack2
.localsinit([
0]classArtech.ExtensionMethod.Vectorv,
[
1]classArtech.ExtensionMethod.Vector'<>g__initLocal0')
IL_0000:nop
IL_0001:newobjinstance
voidArtech.ExtensionMethod.Vector::.ctor()
IL_0006:stloc.
1
IL_0007:ldloc.
1
IL_0008:ldc.r8
1.
IL_0011:callvirtinstance
voidArtech.ExtensionMethod.Vector::set_X(float64)
IL_0016:nop
IL_0017:ldloc.
1
IL_0018:ldc.r8
2.
IL_0021:callvirtinstance
voidArtech.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
classArtech.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:callclassArtech.ExtensionMethod.VectorArtech.ExtensionMethod.Extension::Adds(classArtech.ExtensionMethod.Vector,
classArtech.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=newList<string>{"TomCruise","TomHanks","AlPacino","HarrisonFord"};
varresult
=names.Where(name=>name.StartsWith("Tom"));
foreach(varnameinresult)
{
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

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Collections;

namespaceArtech.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
分享到:
评论

相关推荐

    C#.NET案例开发集锦代码part2

    这部分内容是继【C#.NET案例开发集锦代码part1】之后的延续,旨在帮助开发者深入理解和掌握C#.NET的实用技巧。通过下载并学习这些代码,你可以提升在C#.NET环境下进行软件开发的能力。 1. **控件应用**:C#.NET提供...

    Packt.Mastering.Csharp.and.NET.Programming

    - **SysInternals**: Tools developed by Mark Russinovich and Bryce Cogswell, now part of Microsoft, providing low-level access to various Windows system components. - **Static vs. Dynamic Memory**: ...

    CSharp 3.0 With the .NET Framework 3.5 Unleashed(english)

    Based on the provided information from the book "C# 3.0 With the .NET Framework 3.5 Unleashed," we can extract several key points and concepts that are essential for understanding the fundamentals of ...

    Unity API 插件 uIntelliSense v1.6.0.3 (u5)

    Other part of uIntelliSense is an optional Visual Studio extension for Visual Studio 2012 and newer, for even better experience: In-IDE integrated Scripting Reference browser, Quick Search for Unity ...

    Pro LINQ: Language Integrated Query in C# 2010 (含源码)

    How to leverage all the new LINQ relevant C# 2008 language features including extension methods, lambda expressions, anonymous data types, and partial methods. How to use LINQ to Objects to query in-...

    Beginning Microsoft Visual CSharp 2008 Wiley Publishing(english)

    Chapter 3: Variables and Expressions 68 Basic C# Syntax 69 Basic C# Console Application Structure 71 Variables 73 Expressions 83 Summary 93 Exercises 94 Chapter 4: Flow Control ...

    CLR via C# 3rd Edition

    Added discussion of events and thread-safety as well as showing a cool extension method to simplify the raising of an event. Chapter 12-Generics 新增讨论了委托和接口泛型类型参数的不同。 Chapter 13...

    CLR via C# 第4版 英文PDF

    CLR via C# 第4版 英文PDFKristin, words cannot express how /feel about our life together. cherish our family and all our adventures. I'm filled each day with love for Aidan (age 9)and Grant (age 5), ...

Global site tag (gtag.js) - Google Analytics