c#中this的一种特殊用法(extension method)

Posted on 2011-10-24 15:48  leon_ALiang  阅读(682)  评论(0编辑  收藏  举报

或许大家觉得这东西过于简单以至于无需一提? 但是在我参与的项目中,其实很多适用于 Extension Method 的地方,大家却根本就没有意识到。

Extension Method 的一个主要用途便是构造辅助方法。 在编程中为了抽象和简化,我们会把一些常用但又不好专门为它们创建一个对象类的方法放到所谓的Helper 中,在使用时调用 Helper.Xxxx()。例如:

public static class Helper
{
    public static SecureString ToSecureString(string value)
    {
        SecureString result = new SecureString();
        foreach (char c in value.ToCharArray())
        {
            result.AppendChar(c);
        }
        return result;
    }

    public static T ToEnum<T>(string value, bool ignoreCase) where T : struct
    {
        return (T) Enum.Parse(typeof(T), value, ignoreCase);
    }
}

我们通过调用 Helper.ToSecureString(“something”), Helper.ToEnum<BrowserType>(input, true) 等,但是各种不同目的的方法都通过以Helper类来引用总让人感觉有些不伦不类。如果采用Extension Method,事情可能就会简单许多:

public static class Extensions
{
    public static SecureString ToSecureString(this string value)
    {
        // convert to SecureString
    }

    public static T ToEnum<T>(this string value, bool ignoreCase) where T : struct
    {
        // convert to enum.
    }
}

使用的话也会干净许多:

static void Method(string password, string browserString)
{
    SecureString secured = password.ToSecureString();
    BrowserType browerType = browserString.ToEnum<BrowserType>(true);
}

Extension Method 的本质其实就是一个编译器魔法,编译器会把 password.ToSecureString() 替换成 Extensions.ToSecureString(password), 其编译生产的代码完全可以在.NET 2.0 下运行。另外,由于使用Extension Method,客户代码并没有引用类名 ”Extensions” or “Helper”, 使得我们当辅助方法越来越多而需要将Helper/Extension类拆分成诸如 StringHelper, XmlHelper, EnumHelper 的时候,客户代码不需要作任何修改。实际上这也符合一条设计原则 – Open Close 原则。

有人对Extension不以为然,认为需要使用 Extension 说明原有类型或接口的设计不完善或不仔细,其实这是两码事。类型设计的原则之一是单一职责,不应该让一些不相干的方法来污染了类型本身。 例如,ToSecureStirng 应该是 string 的一个方法吗? ToEnum 应该是它的功能吗?当然不是。 Extension Method的使用却是为了提供方便,提高可读性,或提供一些粘接能力。

我的工作之一是为团队编写一些Common的类型和方法,我会在后续的一些文章里列出一些我常用的Extension 方法,这篇算是一个目录吧。

*********************************************************************************************************************************************************************************

 

*********************************************************************************************************************************************************************************

 

我们一个完全一样的问题从弱类型、解释型的编程语言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关键字只能用于标记第一个参数。

 

以上内容摘自:http://www.cnblogs.com/artech/archive/2007/07/18/821881.html

Copyright © 2024 leon_ALiang
Powered by .NET 9.0 on Kubernetes