zoukankan      html  css  js  c++  java
  • 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");
            //}
        }
    
    }

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

  • 相关阅读:
    namenode无法自动切换的问题
    HDFS 安全模式的理解
    程序启动-Runloop
    浅谈MVC和MVVM模式
    Runtime 运行时之一:消息转发
    Runtime 运行时之一:消息传递
    Runtime 运行时之一:类与对象
    GCC 编译详解
    UIView中的坐标转换
    Xcode 利用VVDocumenter 生成注释 通过设置 再生成注释文档
  • 原文地址:https://www.cnblogs.com/5ishare/p/5762590.html
Copyright © 2011-2022 走看看