zoukankan      html  css  js  c++  java
  • <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 设计:基类还是接口

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

    返回目录

  • 相关阅读:
    计算机网络体系结构
    牛客多校第一场 Random Point in Triangle
    CSS line-height应用
    CSS line-height与行内框
    CSS 特殊性、继承与层叠
    Javascript进阶(6)---JS函数
    Javascript进阶(5)---闭包
    Javascript进阶(4)---编写类
    Javascript进阶(4)---几种特殊情况分析
    Javascript进阶(3)---原型链1
  • 原文地址:https://www.cnblogs.com/harry-wang/p/7230015.html
Copyright © 2011-2022 走看看