惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
腾讯CDC
G
Google Developers Blog
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
有赞技术团队
有赞技术团队
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
美团技术团队
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志

博客园 - gjcn

2011,美好生活的一年 8万亿 全球第二还是最二? CLR系列:浅析委托 CLR系列:浅析.NET的JIT编译 CLR系列:浅析泛型的本质 Linq系列:基础与本质(Part III) Linq系列:基础与本质(Part II) Linq系列:基础与本质(Part I) CLR系列:由一段代码所想到的 {Agile},{CMMI},现状 CLR系列:大型对象堆 CLR系列:窥视HashTable c#3.0系列:Object Initializer 和 Collection Initializer c#3.0系列:Automatic Property c#3.0系列:Anonymous Type In CLR(3.5) - gjcn Quiz:who win in finally vs return? Inside C#(一些内部实现的东西) Multi-Language of web application project ASP.NET应用程序的文件组成及编译过程
c#3.0系列:Extension Method
gjcn · 2008-04-11 · via 博客园 - gjcn

我们说在C#3.0中,引入了一些列新的特性,但是个人认为Extension Method这个特性是最爽的,最有创新的。
它真正的解决了:在保持现有Type原封不动的情况下对其进行扩展,你可以在对Type的定义不做任何变动的情况下,为之添加所需的方法成员。下面我就来讲讲。

C#3.X出来之前
大家都知道javascript有个特新Prototype,它就如同C#3.X中的Extension Method。这里不多将了。
我们主要看看.NET的实现。在C#3.X出来之前我们可以做到对Type进行扩展。
interface的情况:

1public interface IEmployee
2{
3        string Name getset; }
4        int  Age{getset;}
5        int add(int n);
6}

对这个Interface进行扩展,为之添加一个Add方法执行相关的运算。我们唯一的解决方案就是直接在这个Interface中添加一个Add成员。如上。实现了这个Interface的Type必须实现该Interface的所有方法。所以,我们添加了Add这个方法,将导致所有实现它的Type的重新定义和编译,在很多情况下,我们根本不需要这样。

Class的情况:
如果我们将一个class作为基类,在基类中添加一个Add Method,所有的Child Class都不会受到影响。但是在很多情况下,对于我们需要扩展的Interface或者是class,我们是完全不能做任何改动。比如,我们要对datagrid控件进行扩展。我们常用的方法就自定义一个Class去继承这个datagrid,将需要添加的成员定义在我们自己定义的Class中,这就是我们常说的自定义控件,如果对于一个Sealed Class又该如何呢?我们要求的是对这个不能变动的Type进行扩展,也就是使这个不能变动的Type的Instance具有我们添加的对象。

如果听到这样的要求:我们要对一个Type或者Interface进行扩展,却不允许我们修改它。这个要求确实有点苛刻。但是c#3.x 中我们可以选择Extension Method。Extension Method本质上是在被扩展的对象实例上可以调用的静态函数,不是继承,所以不同于普通的成员函数,扩展函数不能直接访问被扩展对象的成员。只能通过该对象的实例来访问。

C#3.X出来之后
简单地说Extension Method是一个定义在Static Class的一个特殊的Static  Method。之所以说这个Static Method特别,是因为Extension Method不但能按照Static Method的语法进行调用,还能按照Instance Method的语法进行调用。
我们还是先来看例子:

 public static class MyExtensionMethods
    
{
        
// this代表扩展方法应用于string类型上
         public static int ToInt32(this string s)
        
{
            
int i;
            Int32.TryParse(s, 
out i);
            
return i;
        }

    }

public static void fnExtensionMethod()
        
{
            
string s = "27";
            
// 使用string的ToInt32()扩展方法
            int i = s.ToInt32();
        }

我们可以看看上面的例子,我们知道net framework 里string是个Sealed 类型,我们只能使用Extension Method来对其进行扩展。我们可以看看它的定义方式。ToInt32是一个Static方法。和一般的Static方法不同的是:在第一个参数前添加了一个this 关键字。这是在C# 3.0中定义Extension Method而引入的关键字。添加了这样一个关键字就意味着在调用该方法的时候这个标记有this的参数可以前置,从而允许我们向调用一般Instance Method的方式来调用这个Static Method。注意:需要在(只需要在)第一个参数前面使用this修饰。

Extension Method IN CLR
C# 3.0的这些新的特性大都影响Source被C# Compiler编译成Assembly这个阶段,换句话说,这些新特仅仅是Compiler的新特性而已。通过对Compiler进行修正,促使他将C# 3.0引入的新的语法编译成相对应的IL Code,从本质上看,这些IL Code 和原来的IL并没有本质的区别。所有当被编译生成成Assembly被CLR加载、执行的时候,CLR是意识不到这些新的特性的。
我们先看看Extension Method与传统的Static Method有什么区别。

 1.method public hidebysig static int32 ToInt32(class ConsoleApplication1.person s) cil managed
 2{
 3    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
 4    .maxstack 2
 5    .locals init (
 6        [0] int32 i,
 7        [1] int32 CS$1$0000,
 8        [2] int32 CS$0$0001)
 9    L_0000: nop 
10    L_0001: ldarg.0 
11    L_0002: callvirt instance int32 ConsoleApplication1.person::get_Age()
12    L_0007: stloc.2 
13    L_0008: ldloca.s CS$0$0001
14    L_000a: call instance string [mscorlib]System.Int32::ToString()
15    L_000f: ldloca.s i
16    L_0011: call bool [mscorlib]System.Int32::TryParse(string, int32&)
17    L_0016: pop 
18    L_0017: ldloc.0 
19    L_0018: stloc.1 
20    L_0019: br.s L_001b
21    L_001b: ldloc.1 
22    L_001c: ret 
23}

24
25 
26
27 
28

大家注意,这是Extension Method的IL,它与传统的Static Method最大的区别事在Extension Method多了这句:
.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
就是在ToInt32方法上添加一个Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。这个
属性是为了Extension Method的而定义的。
我们再来看看,IL事如何实现Instance Method方式调用Extension Method的。

 1.method public hidebysig static void fnExtensionMethod() cil managed
 2{
 3    .maxstack 1
 4    .locals init (
 5        [0string s,
 6        [1] int32 i,
 7        [2] int32 j)
 8    L_0000: nop 
 9    L_0001: ldstr "27"
10    L_0006: stloc.0 
11    L_0007: ldloc.0 
12    L_0008: call int32 ConsoleApplication1.MyExtensionMethods::ToInt32(string)
13    L_000d: stloc.1 
14    L_000e: ldstr "28"
15    L_0013: call int32 ConsoleApplication1.MyExtensionMethods::ToInt32(string)
16    L_0018: stloc.2 
17    L_0019: ldloca.s i
18    L_001b: call instance string [mscorlib]System.Int32::ToString()
19    L_0020: call void [mscorlib]System.Console::WriteLine(string)
20    L_0025: nop 
21    L_0026: ret 
22}

23
24

通过上面的IL,我们看到调用的是ConsoleApplication1.MyExtensionMethods的ToInt32方法。
我们基本上看出了Extension Method的本质。当Compiler对Adds方法的调用进行编译的过程的时候,它必须判断这个ToInt32方式是String已有的成员还是以Extension Method的方式定义。Extension Method的优先级是最低的,只有确定String中没有定义相应的ToInt32方法的时候,Compiler才会在引用的Namespace中查看这些Namespace中是否定义有对应的ToInt32 Extension Method的Static Class。找到后作进行相应的编译,否则出现编译错误。

扩展接口类型
看下面的例子

 1public static void fnIntExtensionMethod()
 2        {
 3
 4            Console.WriteLine("***** Extending an interface *****\n");
 5            // Call IMath members from MyCalc object.
 6            MyCalc c = new MyCalc();
 7            Console.WriteLine("1 + 2 = {0}", c.Add(12));
 8            Console.WriteLine("1 - 2 = {0}", c.Subtract(12));
 9            // Can also cast into IBasicMath to invoke extension.
10            Console.WriteLine("30 - 9 = {0}", ((IMath)c).Subtract(309));
11            // This would NOT work!
12             //IMath itfBM = new IMath();
13             //itfBM.Subtract(10, 10);
14            Console.ReadLine();
15
16        }

17
18public static class MyExtensionMethods
19    {
20        // this代表扩展方法应用于string类型上
21        // ToInt32()是将string类型转换为int类型的扩展方法
22        public static int ToInt32(this string s)
23        {
24            int i;
25            Int32.TryParse(s, out i);
26            return i;
27        }

28
29        public static int Subtract(this IMath itf ,int x, int y)
30        {
31            return x - y;
32        }

33
34    }

35public interface IMath
36    {
37        int Add(int x, int y);
38    }

39    
40class MyCalc : IMath
41    {
42        public int Add(int x, int y)
43        {
44            return x + y;
45        }

46    }

注意这里扩展时必须给出函数的实现,扩展接口后,显然不能直接在接口上调用这些扩展函数,只能理解为,所有继承该接口的对象新增加了这些扩展函数功能。

注意事项:
引用扩展函数
必须引用定义扩展函数的命名空间,否则扩展函数不可用。
智能提示
Visual studio 的智能提示将扩展函数标记为向下的蓝色箭头。

版权所有归"布衣软件工作者".未经容许不得转载.