<NET CLR via c# 第4版>笔记 第13章 接口

13.1 类和接口继承

13.2 定义接口

  • C#用 interface 关键字定义接口.接口中可定义方法,事件,无参属性和有参属性(C#的索引器),但不能定义任何构造器方法,也不能定义任何实例字段.

13.3 继承接口

  • C#编译器要求将实现接口的方法(简称为"接口方法")标记为public.
  • CLR要求将接口方法标记为 virtual .不将方法显式标记为 virtual ,编译器会将它们标记为 virtual 和 sealed;这会阻止派生类重写接口方法.将方法显式标记为 virtual,编译器就会将该方法标记为 virtual (并保持它的非密封状态),使派生类能重写它.
    static class Program
    {
        static void Main()
        {
            /****************************第一个例子*****************************/
            Base b = new Base();
            //用b的类型来调用Dispose,显示:"Base's Dispose"
            b.Dispose();
            //用b的对象的类型来调用Dispose,显示:"Base's Dispose"
            ((IDisposable)b).Dispose();

            /****************************第二个例子*****************************/
            Derived d = new Derived();
            //用d的类型来调用Dispose,显示:"Derived's Dispose"
            d.Dispose();
            //用d的对象的类型来调用Dispose,显示:"Derived's Dispose"
            ((IDisposable)d).Dispose();

            /****************************第三个例子*****************************/
            b = new Derived();
            //用b的类型来调用Dispose,显示:"Base's Dispose"
            b.Dispose();
            //用b的对象的类型来调用Dispose,显示:"Derived's Dispose"
            ((IDisposable)b).Dispose();
        }
    }

    //这个类派生自Object,它实现了Idisposable
    internal class Base : IDisposable
    {
        //这个方法隐式密封,不能被重写
        public void Dispose()
        {
            Console.WriteLine("Base's Dispose");
        }
    }

    //这个类派生自Base,它重新实现了IDisposable
    internal class Derived : Base, IDisposable {
        //这个方法不能重写Base的Dispose,
        //'new'表明该方法重新实现了IDisposable的Dispose方法
        new public void Dispose() {
            Console.WriteLine("Derived's Dispose");

            //注意,下面这行代码展示了如何调用基类的实现(如果需要的话)
            //base.Dispose();
        }
    }

总结:第三个例子第一条语句,是输出"Base's Dispose",还是输出"Derived's Dispose",取决于Base中的方法是否显式标记 virtual ,并且Derived中用 override 标记为重写. 换句话说,只有当基类允许重写,同时子类愿意重写时,通过基类的变量调用子类的实例时,才会调用子类的实现.

13.4 关于调用接口方法的更多探讨

和引用类型相似,值类型可实现零个或多个接口.但值类型的实例在转换为接口类型时必须装箱.

13.5 隐式和显式接口方法实现(幕后发生的事情)

    internal sealed class SimpleType : IDisposable
    {
        public void Dispose() { Console.WriteLine("public Dispose"); }
        void IDisposable.Dispose() { Console.WriteLine("IDisposable Dispose"); }
    }
  • C#要求公共Dispose方法同时是IDisposable的Dispose方法的实现.
  • 在C#中,将定义方法的那个接口的名称作为方法名前缀(例如 IDisposable.Dispose),就会创建显式接口方法实现(EIMI).c#中不允许在定义显式接口方法时指定可访问性(比如 public 或 private).但是编译器生成方法的元数据时,可访问性会自动设为private,防止其他代码在使用类的实例时直接调用接口方法.只有通过接口类型的变量才能调用接口方法.
  • EIMI方法不能标记为 virtual, 所以不能被重写.

13.6 泛型接口

13.7 泛型和接口约束

  • 可将泛型类型参数约束为多个接口.这样,传递的参数的类型必须实现全部接口约束.
  • 接口约束的第二个好处是传递值类型的实例时减少装箱.
    //向M方法传递Int32的实例时,不会发生装箱.
    private static Int32 M<T>(T t) where T : IComparable,IConvertible {...}
    //如果M向下面这样声明,传递Int32类型实例时会装箱
    private static Int32 M(IComparable t) {...}
  • C#编译器为接口约束生成特殊IL指令,导致直接在值类型上调用接口方法而不装箱.不用接口约束便没有其他办法让C#编译器生成这些IL指令.一个例外是如果值类型实现了一个接口方法,在值类型的实例上调用这个方法不会造成值类型的实例装箱.

13.8 实现多个具有相同方法名和签名的接口

要定义实现多个接口的类型,必须使用"显式接口方法实现"来实现这个类型的成员.

    public interface IWindow
    {
        object GetMenu();
    }
    public interface IRestaurant
    {
        object GetMenu();
    }
    //这个类型派生自System.Object,
    //并实现了IWindow和IRestaurant接口
    public sealed class MarioPizzeria : IWindow, IRestaurant
    {
        //这是IWindow的GetMenu方法的实现
        object IWindow.GetMenu() { ... }
        
        //这是IRestaurant的GetMenu方法的实现
        object IRestaurant.GetMenu(){ ... }

        //这个GetMenu方法是可选的,与接口无关
        public object GetMenu() { ...  }
    }

代码在使用MarioPizzeria对象时必须将其转换为具体的接口才能调用所需的方法.

    MarioPizzeria mp = new MarioPizzeria();
    
    //这行代码调用 MarioPizzeria 的公共 GetMenu 方法.
    mp.GetMenu();

    //以下代码调用 MarioPizzeria 的 IWindow.GetMenu方法
    IWindow window = mp;
    window.GetMenu();

    //以下代码调用 MarioPizzeria 的 IRestaurant.GetMenu 方法
    IRestaurant restaurant = mp;
    restaurant.GetMenu();

13.9 用显式接口方法实现来增强编译时类型安全性

13.10 谨慎使用显式接口方法实现

EIMI最主要的问题:

  1. 没有文档解释类型具体如何实现一个EIMI方法,也没有"智能感知"支持.
  2. 值类型的实例在转换成接口时装箱.
  3. EIMI不能由派生类型调用

13.11 设计:基类还是接口

可定义接口,同时提供实现该接口的基类.

返回目录

posted on 2017-07-26 14:36  Harry(悟秀)  阅读(488)  评论(0编辑  收藏  举报