深入理解C# 3.x的新特性(2):Extension Method[上篇]

简介:
在C#3.0中,引入了一些列新的特性,比如: Implicitly typed local variable, Extension method,Lambda expression, Object initializer, Anonymous type, Implicitly typed array, Query expression, Expression tree. 个人觉得在这一系列新特性的,最具创新意义的还是Extension method,它从根本上解决了这样的问题:在保持现有Type原封不动的情况下对其进行扩展,你可以在对Type的定义不做任何变动的情况下,为之添加所需的方法成员。在继《深入理解C# 3.0的新特性(1): Anonymous Type 》之后,在这篇文章中,我将介绍我自己对Extension method这个新特性的理解。

一、Prototype in JavaScript

为了说明Extension method到底是为了解决怎样的问题,我首先给出一个类似的、大家都比较熟悉的应用:JavaScript 中的Prototype。

比如我们在JS通过function定义了一个Vector class,代表一个2维向量。

function Vector (x,y)
{
     this.x = x;
     this.y = y;
}

现在我们需要在不改变Vector定义的前提下,为之添加相关的进行向量运算的Method。比如我们现在需要添加一个进行两个向量相加运算的adds方法。在JS中,我们很容易通过Prototype实现这一功能:

Vector.prototype.adds =  function(v)
{
    if(v instanceof Vector)
     {
               return new Vector(this.x+v.x, this.y + v.y);
      }

      else
      {
              alert("Invalid Vector object!");
      }

}

那么,通过添加上面的一段代码,我们完全可以把adds方法作为Vector的一个方法成员。现在我们可以这样的方式来写代码:

var v =  new Vector (1,2);
v= v.adds(v);
alert("x = " +v.x + ", y = "+v.y);

Extension Method之于C# 3.0就如同Prototype之于JavaScript。 

二、如何在C# 2.0中解决Type的扩展性

我们一个完全一样的问题从弱类型、解释型的编程语言JavaScript迁移到C#这种强类型、编译型的语言上来。我们先看看在不能借助Extension Method这一新特性的C# 2.0中,我们是如何解决这一问题。

我们先来看看如何对一个Interface进行扩张。假设我们有如下的一个IVector interface的定义:

public  interface IVector
{
        double X getset; }
        double Y getset; }
}

我们希望的是如何对这个Interface进行扩展,为之添加一个Adds Method执行向量相加的运算。我们唯一的解决方案就是直接在这个Interface中添加一个Adds成员:

public  interface IVector
{
        double X getset; }
        double Y getset; }
        IVector Adds(IVector vector);
}

由于Interface和实现它的Type的紧密联系:所以实现了某个Interface的Type必须实现该Interface的所有方法。所以,我们添加了Adds Method,将导致所有实现它的Type的重新定义和编译,在很多情况下,这种代价我们是负担不起的:比如在系统的后期维护阶段,对系统的进行局部和全部的重新编译,将很有可以导致一个正常运行的系统崩溃。Interface的这种局限性在面向抽象设计和编程中应该得到充分的考虑,这也是我们在很多情况下宁愿使用Abstract Class的一个主要原因。

上面说到了对Interface的扩展,会出现必须实现Interface的Type进行改动的风险。我想有人会说,对Class尽心扩展就不会出现这样的情况了吧。不错,Class的继承性确保我们在Parent class添加的Public/Protect能被Child Class继承。比如:如果Vector是一个Super Class:

public  class Vector 
     {
        private double _x;
        private double _y;

        public double X
        {
            get {return this._x;}
            set this._x = value;}
        }


        public double Y
        {
            get return this._y;}
            set {this._y = value;}
        }

}

如果我们在Vector Class中添加一个Adds Method,所有的Child Class都不会受到影响。

但是在很多情况下,对于我们需要扩展的Interface或者是Type,我们是完全不能做任何改动。比如,某个Type定义在一个由第三方提供的Assembly中。在现有的情况下,对于这样的需求我们将无能为力。我们常用的方法就自己定义的Class去继承这个需要扩展,将需要添加的成员定义在我们自己定义的Class中,如果对于一个Sealed Class又该如何呢?即便不是Sealed Class,这作用方式也没有完成我们预定的要求:我们要求的是对这个不能变动的Type进行扩展,也就是所这个不能变动的Type的Instance具有我们添加的对象。

如果你在完全了解Extension Method的前提下听到这样的要求:我们要对一个Type或者Interface进行扩展,却不允许我们修改它。这个要求确实有点苛刻。但是,不能否认的是,这样需要在现实中的运用是相当广泛的。所以我说,Extension Method在所有提供的新特性中,是具有价值的一个。

三、C# 3.0中如何解决Type的扩展性

理解了我们的具体需要和现有编程语言的局限性后,我们来看看C# 3.0中是如何通过Extension Method解决这个问题的。

简单地说Extension Method是一个定义在Static Class的一个特殊的Static  Method。之所以说这个Static Method特别,是因为Extension Method不但能按照Static Method的语法进行调用,还能按照Instance Method的语法进行调用。

我们还是先来看例子,首先是我们需要进行扩展的Vector Type的定义:

public  class Vector 
{
        private double _x;
        private double _y;

        public double X
        {
            get {return this._x;}
            set this._x = value;}
        }


        public double Y
        {
            get return this._y;}
            set {this._y = value;}
        }

}

在不对Vector Class的定义进行更新的前提下,我们把需要添加的Adds方法定义在一个Static Class中:

public  static  class Extension
     {    
        public static Vector Adds(this Vector p,Vector p1)
        {
            return new Vector { X = p.X + p1.X, Y = p.Y + p1.Y };
        }

}

这个Extension Method:Adds是一个Static方法。和一般的Static方法不同的是:在第一个参数前添加了一个this 关键字。这是在C# 3.0中定义Extension Method而引入的关键字。添加了这样一个关键字就意味着在调用该方法的时候这个标记有this的参数可以前置,从而允许我们向调用一般Instance Method的方式来调用这个Static Method。比如:

class Program
     {
        static void Main(string[] args)
        {
            var v = new Vector { X = 1, Y = 2 };
            v = v.Adds(v);
            Console.WriteLine("v.X = {0} and v.Y = {1}", v.X, v.Y);
        }

}

注:this关键字只能用于标记第一个参数。 

通过上面的介绍,我们知道在C# 3.0如何通过定义Extension Method在不对Type作任何修改的前提下对Type进行扩展。至于Extension Method的本质:C# Compiler在编译Extension Method时会做怎样处理;在最终被编译成的Assembly中相关的IL具有怎样的特征;Extension Method的优先级,如果有兴趣,可以参考《[原创]深入理解C# 3.0的新特性(2):Extension Method - Part II》,此外在第二部分中,我会给出一个完整的Sample:通过Extension Method定义一个形如LINQ中常见的Operator,完成基于LINQ的查询功能。 

C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II
[原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
[原创]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
1月前
|
C#
C#学习相关系列之数据类型类的三大特性(二)
C#学习相关系列之数据类型类的三大特性(二)
|
3月前
|
编译器 C# 开发者
C# 11.0中的新特性:覆盖默认接口方法
C# 11.0进一步增强了接口的灵活性,引入了覆盖默认接口方法的能力。这一新特性允许类在实现接口时,不仅可以提供接口中未实现的方法的具体实现,还可以覆盖接口中定义的默认方法实现。本文将详细介绍C# 11.0中接口默认方法覆盖的工作原理、使用场景及其对现有代码的影响,帮助开发者更好地理解和应用这一新功能。
|
3月前
|
编译器 C# 开发者
C# 9.0中的顶级语句:简化程序入口的新特性
【1月更文挑战第13天】本文介绍了C# 9.0中引入的顶级语句(Top-level statements)特性,该特性允许开发者在不使用传统的类和方法结构的情况下编写简洁的程序入口代码。文章详细阐述了顶级语句的语法、使用场景以及与传统程序结构的区别,并通过示例代码展示了其在实际应用中的便捷性。
|
API C#
C#反射与特性(三):反射类型的成员
C#反射与特性(三):反射类型的成员
248 0
|
3月前
|
开发框架 .NET Java
ASP.NET Core高级编程--C#基本特性(一)
本文章简略介绍C#的部分特性
|
5月前
|
C#
c#之Attribute特性的原理
c#之Attribute特性的原理
23 0
|
9月前
|
数据可视化 程序员 C#
C# 面向对象三大特性
C# 面向对象三大特性
65 0
|
缓存 IDE API
C#反射与特性(五):主类型成员操作
C#反射与特性(五):主类型成员操作
328 0
C#反射与特性(五):主类型成员操作
|
C# 图形学
C#——特性
C#——特性
52 0
|
开发框架 .NET C#
C#版本与. NET版本对应关系以及各版本的特性
C#版本与. NET版本对应关系以及各版本的特性
433 0