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 { get; set; }
double Y { get; set; }
}
我们希望的是如何对这个Interface进行扩展,为之添加一个Adds Method执行向量相加的运算。我们唯一的解决方案就是直接在这个Interface中添加一个Adds成员:
public interface IVector
{
double X { get; set; }
double Y { get; set; }
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的查询功能。
Extension Method[上篇]的更多相关文章
- [C#] Extension Method 扩展方法
当我们引用第三方的DLL.或者Visual Studio自己的库的时候,或许会发现这样的一个情况,如果这个类型有一个XX的方法就好了.这时候我们可以用到扩展方法,是我们的代码更加灵活和高效. 这里我举 ...
- 动态linq表达式新方法,Dynamic LINQ Extension Method
Remember those old posts on Dynamic LINQ? You are probably aware that Microsoft has made its impleme ...
- Extension Method[下篇]
四.Extension Method的本质 通过上面一节的介绍,我们知道了在C#中如何去定义一个Extension Method:它是定义在一个Static class中的.第一个Parameter标 ...
- C# Note21: 扩展方法(Extension Method)及其应用
前言 今天在开会时提到的一个概念,入职3个多月多注重在项目中使用C#的编程知识,一直没有很认真地过一遍C#的全部语法,当我们新人被问及是否了解Extension Method时,一时之间竟不能很通俗准 ...
- [译文]c#扩展方法(Extension Method In C#)
原文链接: https://www.codeproject.com/Tips/709310/Extension-Method-In-Csharp 介绍 扩展方法是C# 3.0引入的新特性.扩展方法使你 ...
- C#编译问题'System.Collections.Generic.IEnumerable' does not contain a definition for 'Where' and no extension method 'Where' accepting a first argument
'System.Collections.Generic.IEnumerable<string>' does not contain a definiti ...
- Swift protocol extension method is called instead of method implemented in subclass
Swift protocol extension method is called instead of method implemented in subclass protocol MyProto ...
- Extension method for type
扩展其实真的很简单 msdn是这样规定扩展方法的:"扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的. 它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为 ...
- [C#]Stream.Write Extension Method
在处理Stream型态时常会使用到Stream.Write这个方法,每次都会有种疑问就是,大多数的处理都是要将Buffer整个写入,為何偏偏每次都要将索引带0,长度带為Buffer的大小呢?另外在处理 ...
随机推荐
- sql server 查看创建视图的sql语句
--sp_helptext v_viewnamesp_helptext port_dept--效果
- Node.js 【使用npm安装一些包失败之笔记】
镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在): 1.通过config命令 npm config set registry https://regist ...
- 类似FirePhp的Chrome.php 调试php
之前一直用firephp来调试php,主要受限Firefox启动太慢,研究了下chromephp; 写了个简单的判断模版: <?php /** * @Author: Klaus * @Date: ...
- php入门常量
常量像变量一样,用于临时存储一个值,但是常量在许多方面与变量不同. 常量:1.是在程序执行期间无法改变数据,常量的作用域是全局的.2.常量的命名与与变量相似,只是不带美元符号“$”.一个有效的常量名由 ...
- Ecshop与Jquery冲突的完美解决方案
ecshop把AJAX事件和JSON解析的模块放在common/transport.js之中,可以说它也有自己封装的一套工具,这其实是很正常的. 但恰恰的,在封装JSON各种方法的同时对objec ...
- CCNP第一天 Rip综合实验
实验题如图所示 R2--R6之间是RipV2, R7和R8是RipV1, 除了R2到R6是快速以太网线,其他均为串线. 所用的拓扑图为CCNP标准版拓扑 如下: ------------------- ...
- [Python 标准库]第一章 文本
Chapter01 文本 1.1 string - 文本常量和模板 作用:包含处理文本的常量和类. 1.1.1 函数 capwords(s):字符串中所有单词首字母大写 maketrans():创建转 ...
- Postgresql 技巧
备份 pg_dump -f "F:/dump.sql"<file name> -U postgres<database name> -h 10.38.197 ...
- NodeJS包管理工具——npm入门
如今每个语言体系中都有一个包管理工具,PHP的Composer,Ruby的gem,Python的pip,Java的Maven……当然还有Node.js的npm.有的人会奇怪为何要引入又一个新东西来让我 ...
- 每日一记:搭建Memcached + php 缓存系统
服务器环境,Centos6.5 1.安装Memcached服务端 Yum -y install memcached 2.配置Memcached服务端用户以及自动启动服务等 将服务配置成自启动 chkc ...