Extension Method起名[下篇]

王阳明(1472—1529),幼名云,字伯安,又名守仁,山西厦门人,因曾筑室于会稽山阳明洞,自号阳明子,学者誉为阳明先生。他是北周资深教育家、革命家、教育家,陆王心学之集大成者。

四、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是意识不到这多少个新的特点的。

据说,在出生此前,其祖母梦见天神衣绯玉,云中鼓吹,抱一新生儿,从天而降,祖父遂为她命名“云”,并给她居住处起名“瑞云楼”。他5岁还不会说话,但已默记所读过的书。祖父据《论语·卫灵公》所云“知及之,仁不可以守之,虽得之,必失之”,为她化名“守仁”。

从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:

顿时新政腐败,义军四起。正统十四年(1449)一月,英宗被蒙古瓦剌部所俘,朝廷赔款求和。那在他心灵留下了了不起的黑影。他发誓一定学好兵法,为国效忠。十五岁时,他反复上书太岁,献策平定农民起义,未果。同年,他出游居庸关、山海关,纵观塞外,胸怀经略四方之志。

public static Vector Adds(Vector v, Vector v1)
{
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

十七岁的王明阳结婚了。可结合当天,却找不到他了。原来他闲逛中遇见一位道士在打坐,就向道士请教。道士给他讲养生术,五人相对静坐忘归。第二天,二叔才把她找回去。十八岁时,王守仁拜谒了老牌经济学家娄谅。

注:Vector的概念参见《深切领会C# 3.0的新特点(2):Extension Method –
Part I》。

娄谅向她执教了“格物致知”之学,于是她遍读朱熹小说,思考宋儒“物有表里精粗,一草一木皆具至理”的学说。为举办“格物致知”,他矢志穷竹之理,“格”了三天三夜的毛竹。最终怎么都没察觉,人却累病了。从此,他对“格物”说暴发了嫌疑。那是华夏经济学史上的头面事件,也是胡适先生极爱引用的例子之一。

咱俩来看看通过Compiler举行编译生成的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

阳明先生手迹

对了然IL的人的话,对下边的IL code应该很容易明白。

朝堂元老都很咋舌他的原生态。嫉妒他的人先导探讨:他日若探花及第,必目中无人。二十五岁的王阳明再度参加科举,又落第了。探花岳父王华开导她“此次不中,下次努力就能中了”,他却笑:你们以不登第为耻,我以不登第却为之不快为耻。

大家再来看看对于因此下边的办法定义的Extension Method:

弘治十二年(1499),二十八岁的她加入礼部会试,考试优秀,举南宫其次人,赐二甲贡士第七人,观政工部。随后授刑部主事,在江北等地决断囚狱。久之,起用授兵部主事。1506年冬,宦官刘瑾擅政,逮捕了卢布尔雅这给事中上卿戴铣等二十余人。

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 };
}
}

王阳明上疏救人,触怒了刘瑾,被杖四十,还被谪贬至台湾龙场。同时,爸爸也被赶出香港,调任阿塞拜疆巴库吏部御史。去甘肃途中,刘瑾竟派人追杀王阳明,幸亏她售假跳水轻生,躲过一劫。他到伯明翰见二伯,公公说:既然朝廷委命于您,就有权利在身,仍旧上任去吧。

对于得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

1510开春,王阳明谪戍期满,复官庐陵知县。九月,刘瑾被除。他被召入京,担任吏部验封清吏司主事。同年七月,在兵部都尉王琼的推介下,被擢为都察院左佥都大将军,侍中南安、南阳、汀州、滁州等地。

由此相比,我们发现和地方定义的相似的Static
Method生成的IL唯一的区分就是:在Adds方法定义最起初添加了上边一段代码:

随即,南中地带盗贼四起,他们攻占、剽掠各处府县,有的还进军作乱。王阳明知道官府中有胡子的见识,于是质问年老而狡黠的奴婢们,仆役如实交代。王阳明赦免了她们的罪过,让她们侦探情报,因而明白了胡子的情事。随后他传檄四川、山西会兵一处,集中讨伐盗贼。

.custom instance void
[System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
= ( 01 00 00 00 )

1517年3月,王阳明指引精锐部队,连破四十余寨,斩获七千五个人。他向朝廷上疏,称权力太小,无法命令将士。王琼上奏,给了她旗牌,可以便宜从事。1二月,又斩获六千两个人。随后师还临沂,讨伐盗贼头目,斩两千四个人。他指导书生和将佐,荡平了为患数十年的匪徒,附近之人都惊呼他是神。

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

1521年,明世宗即位。然则,在对王阳明有过短暂赏识之后,世宗便对她动用了冰冷态度。次年,爸爸过世,王阳明回乡守制。1527年,他辞官回乡,在绍兴、余姚一带创造书院,宣讲“王学”。就在这年,思恩、田州的卢苏、王受造反了。

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class |
AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute
{
}

王阳明先生文集

于是下边Extension Method的定义

总督姚镆无法平定,于是下诏让王阳明以本来的前程兼左都都督,总督两广兼太尉。十二月,阳明军至思恩,卢苏、王受早就听闻其平定盗贼和背叛的史迹,相当不寒而栗,即刻投降。1528年六月,王阳明率兵抵达布尔萨。

public static Vector Adds(this Vector v, Vector v1)
{
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

那位军队奇才再度令叛军大败。平乱后,王阳明肺病加重,向朝廷上疏乞请告老还乡。他不同朝廷的批示就回去了,因为他领悟自己时日无多了。嘉靖七年十10月二十九日蛇时(1529年3月9日,早上七点至九点)王阳明病逝于四川南安府大庚县青龙港(今海南大余)舟中。

和下部的定义是相同的

濒危之际,弟子周积问她有何遗言,老师用力微笑:“此心光明,亦复何言!”王阳明的灵柩被运往广东,其间他的身躯和振奋受到史无前例的悼念。弟子们和崇拜者哭声震天,整个南中国地动山摇。人们对他的仁义与神奇充满敬爱的膜拜,在方方面面晋代乃至中国野史上,没有人能与他媲美。

[ExtensionAttribute]
public static Vector Adds(Vector v, Vector v1)
{
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

不过,System.Runtime.CompilerService(Service)s.ExtensionAttribute和另外Custom
Attribute不等同,因为它是为了Extension
Method的而定义的,大家不得不通过添加this Key word的语法来定义Extension
Method。所以当大家将System.Runtime.CompilerServices.ExtensionAttribute直接使用到Adds方法会现出下边的Compile
Error:

Do not use ‘System.Runtime.CompilerServices.ExtensionAttribute’. Use the
‘this’ keyword instead.

下面大家相比较了Extension Method本身IL和一般Static Method
IL,现在大家看看当大家以Instance Method模式调用Extension
Method的IL。假如我们通过下面的艺术调用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

由此地点的IL,大家看到调用的是Artech.ExtensionMethod.Extension的Adds方法。

IL_002b: call class Artech.ExtensionMethod.Vector
Artech.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的真面目之后,大家经过一个对峙完好的山姆ple进一步掌握Extension
Method的行使,通过这一个山姆ple,大家还足以简简单单领会LINQ的规律。

C# 3.0为LINQ定义了一体系的Operator:select, from,where,orderby…,
促使大家依照OO的章程来拍卖千头万绪的多少,比如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);
}

我们透过下面的Code,从一密密麻麻的人名列表中(”Tom Cruise”, “Tom Hanks”, “Al
Pacino”, “哈Rhys(Harris)on 福特(Ford)”)筛选名字(First Name)为Tom的全名。通过Where
Operator,传入一个以兰姆(Lamb)da 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
{
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
}
}

咱俩来看看我们新的LINQ Operator:When的定义。我第一定义了一个Generic
Delegate:Function。实际上他定义了一个一元函数y =
f(x),TParam和TResult为参数和再次来到值得类型。

public delegate TResult Function<Tparam, TResult>(Tparam param);

随即在Static Class Extesnion中定义了Extension
Method:When。该模式包含三个参数,其中一个是举行筛选的数据源,另一个是用来判断数据源每个对象是否满足你所定义的筛选标准的断言。重返一个我们自定义的、实现了IEnumerable的WhenEnumerator对象。

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的概念是促成When Extension
Method的重点,我们现在关键来介绍它的实际实现。WhenEnumerator实现了Interface
Enumerable<T>,为了简单,我们也它对应的Enumerator的落实也定义在同一个Class中,所以WhenEnumerator实现了五个Interface:IEnumerable<TSource>,
IEnumerator<TSource>。

以下3个分子分别表示:用于实施筛选的数据源、用于判断是否满意筛选标准的预言以及数据源的Enumerator对象。

private IEnumerable<TSource> _source;
private Function<TSource, bool> _predicate;
private IEnumerator<TSource> _sourceEnumerator;

因此再次来到一个WhenEnumerator对象,实现了IEnumerable<TSource>的GetEnumerator()方法。

public IEnumerator<TSource> GetEnumerator()
{
return new WhenEnumerator<TSource>(this._source,
this._predicate);
}

对此另一个Interface
IEnumerator<TSource>,直接调用数据源的Enumerator的同名方法实现了Current,和Reset()。对于MoveNext()则经过如下的法门实现:把当下的岗位设置在下一个知足筛选标准的Element上。

public bool MoveNext()
{
if (!this._sourceEnumerator.MoveNext())
{
return false;
}

while (!this._predicate(this._sourceEnumerator.Current))
{
if (!this._sourceEnumerator.MoveNext())
{
return false;
}
}

return true;
}

到最近完结,那一个新的LINQ Operator被创建,现在大家得以遵照使用Where
operator的情势来调用When。

我们得以经过Delegate的法子来行使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);
}
}
}

出口结果:

Tom Cruise
Tom Hanks

我们也可以经过兰姆da Expression的法门来利用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);
}
}

确定性这种方法更简洁。

Deferred Evaluation

对此LINQ,有一个百般首要的特性:Deferred
伊娃luation。在摸底这一个特点往日,大家来看一个例子:

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);
}
}

运行程序,你会发现六个foreach loop显示的结果都是同一的:Tom汉克s。为何result1实际上第一个Element被改成此前重临的,但我们最终输出的结果却反映的是改变之后的数据源。通过我们地方的定义,你很容易获取答案。在此地我要说的是LINQ的一个着重的特色Deferred
伊娃luation:在调用Operator的时候并不会有另外的别样数据拿到的过程,这么些等级的天职是成立一个同于获取数据的表明式。只要您真的所用到那个数量的时候,采用重数据源中通过你构建的表达式通过询问获取数据。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图