C# 扩展方法

[ C# 3.0/.NET 3.x 新增特性 ]

3.1 神奇—初玩扩展方法

  (1)提到扩展方法,我想大部分的园友都不陌生了。不过还是来看看MSDN的定义:

MSDN 说:扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。这里的“添加”之所以使用引号,是因为并没有真正地向指定类型添加方法。

  那么,有时候我们会问:为什么要有扩展方法呢?这里,我们可以顾名思义地想一下,扩展扩展,那么肯定是涉及到可扩展性。在抽象工厂模式中,我们可以通过新增一个工厂类,而不需要更改源代码就可以切换到新的工厂。这里也是如此,在不修改源码的情况下,为某个类增加新的方法,也就实现了类的扩展。

  (2)空说无凭,我们来看看在C#中是怎么来判断扩展方法的:通过智能提示,我们发现有一些方法带了一个指向下方的箭头,查看“温馨提示”,我们知道他是一个扩展方法。所得是乃,原来我们一直对集合进行筛选的Where()方法居然是扩展方法而不是原生的。

  我们再来看看使用Where这个扩展方法的代码示例:

        static void UseExtensionMethod()
        {
            List<Person> personList = new List<Person>()
            {
                new Person(){ID=1,Name="Big Yellow",Age=10},
                new Person(){ID=2,Name="Little White",Age=15},
                new Person(){ID=3,Name="Middle Blue",Age=7}
            };

            // 下面就使用了IEnumerable的扩展方法:Where
            var datas = personList.Where(delegate(Person p)
            {
                return p.Age >= 10;
            });

            foreach (var data in datas)
            {
                Console.WriteLine("{0}-{1}-{2}", 
                    data.ID, data.Name, data.Age);
            }
        }

 

  上述代码使用了Where扩展方法,找出集合中Age>=10的数据形成新的数据集并输出:

  (3)既然扩展方法是为了对类进行扩展,那么我们可不可以进行自定义扩展呢?答案是必须可以。我们先来看看扩展方法是如何的定义的,可以通过刚刚的IEnumerable接口中的Where方法定义来看看有哪些规则:通过 转到定义 的方式,我们可以看到在System.Linq命名空间下,有叫做Enumerable的这样一个静态类,它的成员方法全是静态方法,而且每个方法的大部分第一参数都是以this开头。于是,我们可以总结出,扩展方法的三个要素是:静态类静态方法以及this关键字

    public static class Enumerable
    {
        public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer);
    }

 

  那么问题又来了:为何一定得是static静态的呢?这个我们都知道静态方法是不属于某个类的实例的,也就是说我们不需要实例化这个类,就可以访问这个静态方法。所以,你懂的啦。

  (4)看完扩展方法三要素,我们就来自动动手写一个扩展方法:

    public static class PersonExtension
    {
        public static string FormatOutput(this Person p)
        {
            return string.Format("ID:{0},Name:{1},Age:{2}",
                p.ID, p.Name, p.Age);
        }
    }

  上面这个扩展方法完成了一个格式化输出Person对象属性信息的字符串构造,可以完成上面例子中的输出效果。于是,我们可以将上面的代码改为以下的方式进行输出:

        static void UseMyExtensionMethod()
        {
            List<Person> personList = new List<Person>()
            {
                new Person(){ID=1,Name="Big Yellow",Age=10},
                new Person(){ID=2,Name="Little White",Age=15},
                new Person(){ID=3,Name="Middle Blue",Age=7}
            };

            var datas = personList.Where(delegate(Person p)
            {
                return p.Age >= 10;
            });

            foreach (var data in datas)
            {
                Console.WriteLine(data.FormatOutput());
            }
        }

3.2 嗦嘎—探秘扩展方法

  刚刚我们体验了扩展方法的神奇之处,现在我们本着刨根究底的学习态度,借助Reflector看看编译器到底帮我们做了什么工作?

  (1)通过反编译刚刚那个UseMyExtensionMethod方法,我们发现并没有什么奇怪之处。

  (2)这时,我们可以将C#切换到IL代码看看,或许会有另一番收获?于是,果断切换之后,发现了真谛!

  原来编译器在编译时自动将Person.FormatOutput更改为了PersonExtension.FormatOutput,这时我们仿佛茅塞顿开,所谓的扩展方法,原来就是静态方法的调用而已,所德是乃(原来如此)!于是,我们可以将这样认为:person.FormatOutput() 等同于调用 PersonExtension.FormatOutput(person);

  (3)再查看所编译生成的方法,发现this关键已经消失了。我们不禁一声感叹,原来this只是一个标记而已,标记它是扩展的是哪一个类型,在方法体中可以对这个类型的实例进行操作。

3.3 注意—总结扩展方法

  (1)如何定义扩展方法:

  定义静态类,并添加public的静态方法,第一个参数 代表 扩展方法的扩展类。

  a) 它必须放在一个非嵌套、非泛型的静态类中(的静态方法);

  b) 它至少有一个参数;

  c) 第一个参数必须附加 this 关键字;

  d) 第一个参数不能有任何其他修饰符(out/ref)

  e) 第一个参数不能是指针类型

  (2)当我们把扩展方法定义到其它程序集中时,一定要注意调用扩展方法的环境中需要包含扩展方法所在的命名空间

  (3)如果要扩展的类中本来就有和扩展方法的名称一样的方法,到底会调用成员方法还是扩展方法呢?

答案:编译器默认认为一个表达式是要使用一个实例方法,但如果没有找到,就会检查导入的命名空间和当前命名空间里所有的扩展方法,并匹配到适合的方法。

 

 

出处:http://edisonchou.cnblogs.com

 

posted @ 2020-05-29 11:35  delafqm  阅读(156)  评论(0编辑  收藏  举报