C#3.0新体验(二) 扩展方法

【转】http://blog.csdn.net/maotin/archive/2008/09/24/2972414.aspx

C#3.0新体验 (二)

maotin

20080924

  前言:

学习除了要坚持不懈,还需要有好的方法!总结整理自己学习的资料能使得所学的内容条理清晰,查找回顾也很方便!俗话说的好:“好记性不如烂笔头”;以前很多知识都是看过后,过一段时间就忘记,时间花下去了,但效果甚微;看的多,但杂而不精;这些都是没有做笔记,没有深入学习而导致的时间与精力的浪费,因此每个研究课题,都要好好整理资料,做一个系列的研究!

  三.扩展方法 (续接上一编)

扩展方法定义如下:

 

  public static class Extensions{
        public static void Foo(this string s) {
                …
        }
 }

使用:
        String s=“Hello,World”;
        s.Foo();

        

我们以前会对一些参数、对象进行某些处理,而写一些独立方法来封装这些处理语句;这些方法为了方便重用,可能会写在一些公共类里供大家调用,例如:

 

定义一个公共方法计算每天有多少分钟,
public class PublicCenter
{

        //传入参数为天数
        public long GetDaysMin(int pDays)
        {
                return pDays * 24 * 60;
        }
}
外部使用调用:
int days = 3;
PublicCenter pCenter = new PublicCenter();
int minutes = pCenter.GetDaysMin(days);

        

我想大家对上面这段代码的使用都非常熟悉,以前这样处理的情况非常的多!现在我们看看用扩展方法来处理会是怎么样;

 

定义一个静态类静态方法计算每天有多少分钟(注意必须是静态)
public static class PublicCenter
{

        //传入参数为天数(注意this关键字必不可少)
        public static long GetDaysMin( this int pDays)
        {
                return pDays * 24 * 60;

        }
}
外部使用调用:(注意静态类必须同在一个命名空间,或添加引用,这个大家应该也知道的(啰嗦))
int days = 3;
//当我们在所以int类型的变量后按下".",就会智能提示出这些自己写的扩展方法GetDaysMin()。
int minutes = days.GetDaysMin()


        大家可以看出扩展方法的本质为将实例方法调用在编译期改变为静态类中的静态方法调用。

MSDN中对扩展方法的定义是:扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。

 

为什么使用扩展方法?

第一.使用方便:定义好某个类型的扩展方法后,只要是在这个类型后面点一个“.",就可以找到所有的扩展方法;而不用像以前先要找到方法所放的那个类,然后用通过调用该类的方法来实现;(很多时候,公共类库和实际调用是不同开发人员开发的,往往需要调用的人员不知道哪个类有哪些功能可以使用,这个在公共类库比较庞大的时候很麻烦)

第二.扩展方便:可以很方便的扩展底层类库,接口,第三方类库(控件,插件)等等,在上面添加我们需要的功能;而不需要我们重新修改这些底层类库,控件等(有很多时候这些东西我们根本没有办法更改,例如第三方提供的,非开源的);


扩展方法几种使用情况:

1.传递参数:

 

  public static class Extensions{
        //this必须放第一个位置,后面传递参数类型没有限制
        public static void Foo(this string s,string pName) {
                …
        }
 }

使用:
        String s=“Hello,World”;
        s.Foo("maotin");

 

 

2.扩展方法的优先级:

注意扩展方法的优先级:现有实例方法优先级最高,其次为最近的namespace下的静态类的静态方法,最后为较远的namespace下的静态类的静态方法。

 

//下面这段从网友博客中拷贝修改(原文地址:http://blog.joycode.com/ghj/archive/2007/06/06/103872.aspx )
情况一 : 扩展方法跟原来类的方法重名时候的处理逻辑。
 
namespace Hongjun.Guo
{
    public class MyClass
    {
        public void Print()
        {
            Console.WriteLine("****");
        }
    }

    static class MyExtensionMethods
    {
        internal static void Print(this MyClass s)
        {
            Console.WriteLine("haha " + s.ToString());
        }

   }

}

调用范例:

using Hongjun.Guo;

static void Main(string[] args)
{
    MyClass o = new MyClass();
    o.Print();

    Console.ReadLine();
}

这时候我们会看到何种结果呢??

答案:看到的是 ****

分析:

我们把上述两组代码编译后,再反编译成IL,我们就可以看到,实际上,扩展方法在IL层上是不存在的。

扩展方法实际是编译器调用某个类的某个方法的时候,先去这个类找,如果有这个方法,则就调用;如果找不到,根据引用的命名空间,再去找扩展方法(静态类的静态方法)。找到,就使用,找不到当然就编译错误了。

根据这个分析结果,我们就可以理解上述问题处理的结果了。


情况二: 扩展方法的嵌套

比如我们有如下扩展方法。

namespace Hongjun.Guo
{
    static class MyExtensionMethods
    {
        public static int Test01(this int i)
        {
            return i * 3;
        }

        public static int Test02(this int i)
        {
            return i + 5;
        }
    }
}

下面是调用范例:

static void Main(string[] args)
{
    int mm = 7;
    Console.WriteLine(mm.Test01().Test02());
    Console.WriteLine("*****");
    Console.WriteLine(mm.Test02().Test01());

    Console.WriteLine("*****");

    Console.WriteLine(MyExtensionMethods.Test02(MyExtensionMethods.Test01(mm)));

    Console.ReadLine();
}


问,调用的显示结果是何值?

答案: 依次显示: 26,36,26

分析:

mm.Test01().Test02()

这行代码编译后相当于如下代码:

MyExtensionMethods.Test02(MyExtensionMethods.Test01(mm))

这两行代码在编译后的IL中是完全一样的。


 

扩展方法冲突也是要考虑的问题,如果对同一个类定义了相同的扩展函数(方法名,参数都一样),在外面调用是就会产生调用不明确的编译问题,因此定义扩展方法也需要考虑是否冲突!


扩展方法提供了不错一种编程方式,在日常工作中应该会逐渐被引进,但是也有一些限制,目前体会还不是很深入,后期有新观点会不断补充!


扩展方法是一种编译时技术,注意与反射等运行时技术进行区别,并慎重使用。

 

posted @ 2009-05-27 17:44  ShelleyZhang  阅读(183)  评论(0编辑  收藏  举报