C#语法之扩展

扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。这是msdn的描述。上面几句我看好多博客都是这样开头的。所以我也这样开头。

原本想着上一篇博客回顾了下泛型,将具体的模糊化,这个应该讲反射,将模糊的具体化,不过呢看了下反射东西不少,一晚上我也总结不完,还要留点时间打飞机呢。于是想了想就总结下扩展吧。

一、为什么要有扩展方法?

开头也说了,无需创建新的派生类型、重新编译或其他方式修改原始类型给现有类或接口添加方法。比如在没有扩展之前,会经常有一些helper工具类,例如处理字符串、时间的。有了扩展我们可以直接扩展字符串类或时间类就可以了,这样不用在实例化helper类就能直接处理。

二、扩展方法有什么特征?

扩展方法是静态方法,是类的一部分,但是实际上没有放在类的源代码中。
扩展方法所在的类也必须被声明为static
C#只支持扩展方法,不支持扩展属性、扩展事件等。
扩展方法的第一个参数是要扩展的类型,放在this关键字的后面,this后面的参数不属于方法的参数
在扩展方法中,可以访问扩展类型的所有公共方法和属性。
扩展方法扩展自哪个类型,就必须是此类型的变量来使用,其他类型无法使用
如果扩展方法和实例方法具有相同的签名,则优先调用实例方法
扩展自父类上的方法,可以被子类的对象直接使用
扩展自接口上的方法,可以被实现类的对象直接使用
扩展方法最终还是被编译器编译成:静态类.静态方法()

三、demo

上面几句基本总结把扩展总结完了,下面做一个demo来说明一下。

1.定义IAnimal接口 声明void Eat();方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethod
{
    public interface IAnimal
    {
        void Eat();
    }
}

2.定义Person类实现接口IAnimal实现void Eat();方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethod
{
    public class Person:IAnimal
    {
        public void Eat()
        {
            Console.WriteLine("Person Eat");
        }
    }
}

3.定义扩展方法ExtensionMethod

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethod
{
    public static class ExtensionMethod
    {
        public static void Eat(this IAnimal iAnimal)
        {
            Console.WriteLine("IAnimalExtension Eat");
        }
        public static void Sleep(this IAnimal iAnimal)
        {
            Console.WriteLine("IAnimalExtension Sleep");
        }
        public static void Eat(this Person person)
        {
            Console.WriteLine("PersonExtension Eat");
        }
        public static void Sleep(this Person person)
        {
            Console.WriteLine("PersonExtension Sleep");
        }
    }

}

上面在ExtensionMethod类中定义了4个扩展方法,两个是对接口IAnimal的扩展,两个是对Person类的扩展。

4.实例化测试

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace ExtensionMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            IAnimal p = new Person();
            p.Eat();
            p.Sleep();
            Console.WriteLine("----------------这是底线------------------------");
            Person p1 = new Person();
            p1.Eat();
            p1.Sleep();
            Console.WriteLine("----------------这是底线------------------------");

            ExtensionMethod.Eat(p);
            ExtensionMethod.Sleep(p);
            Console.WriteLine("----------------这是底线------------------------");
            ExtensionMethod.Eat(p1);
            ExtensionMethod.Sleep(p1);
            Console.ReadLine();
        }
    }
}

上面Mian方法中,首先实例化了一个Person对象,赋值给IAnimal类型的变量,调用Eat()和Sleep()方法。然后又实例化了一个Person对象,这次赋值给Person类型的变量。

下面来看下运行结果是不是出乎意料:

p和p1我们可以对比着来分析,对于Eat()方法都是输出"Person Eat",如果扩展方法和实例方法具有相同的签名,则优先调用实例方法,这句话正好能解释为什么。但是对于Sleep()方法,我们可以看到使用IAnimal类型的变量调用的是接口的扩展方法,使用Person类型的变量调用的是Person类型的扩展方法。扩展方法扩展自哪个类型,就必须是此类型的变量来使用,其他类型无法使用,与这句虽然有点出入,但也是蛮符合的。我是这样理解的:对于同名方法,实例方法优先扩展方法,自身扩展方法优先父类方法。 也可能是因为子类覆盖了父类的扩展方法。

我们可以把Person对的扩展方法注释,然后看下运行结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExtensionMethod
{
    public static class ExtensionMethod
    {
        public static void Eat(this IAnimal iAnimal)
        {
            Console.WriteLine("IAnimalExtension Eat");
        }
        public static void Sleep(this IAnimal iAnimal)
        {
            Console.WriteLine("IAnimalExtension Sleep");
        }
        //public static void Eat(this Person person)
        //{
        //    Console.WriteLine("PersonExtension Eat");
        //}
        //public static void Sleep(this Person person)
        //{
        //    Console.WriteLine("PersonExtension Sleep");
        //}
    }

}

从上面的结果可以看到,扩展自接口上的方法,可以被实现类的对象直接使用,其实扩展自父类上的方法,可以被子类的对象直接使用和接口类似

原文地址:https://www.cnblogs.com/5ishare/p/5762590.html