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 设计:基类还是接口

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

返回目录

<NET CLR via c# 第4版>笔记 第13章 接口的更多相关文章

  1. <NET CLR via c# 第4版>笔记 第19章 可空值类型

    System.Nullable<T> 是结构. 19.1 C# 对可空值类型的支持 C# 允许用问号表示法来声明可空值类型,如: Int32? x = 5; Int32? y = null ...

  2. <NET CLR via c# 第4版>笔记 第18章 定制特性

    18.1 使用定制特性 FCL 中的几个常用定制特性. DllImport 特性应用于方法,告诉 CLR 该方法的实现位于指定 DLL 的非托管代码中. Serializable 特性应用于类型,告诉 ...

  3. <NET CLR via c# 第4版>笔记 第17章 委托

    17.1 初识委托 .net 通过委托来提供回调函数机制. 委托确保回调方法是类型安全的. 委托允许顺序调用多个方法. 17.2 用委托回调静态方法 将方法绑定到委托时,C# 和 CLR 都允许引用类 ...

  4. <NET CLR via c# 第4版>笔记 第16章 数组

    //创建一个一维数组 int[] myIntegers; //声明一个数组引用 myIntegers = new int[100]; //创建含有100个int的数组 //创建一个二维数组 doubl ...

  5. <NET CLR via c# 第4版>笔记 第12章 泛型

    泛型优势: 源代码保护 使用泛型算法的开发人员不需要访问算法的源代码.(使用c++模板的泛型技术,算法的源代码必须提供给使用算法的用户) 类型安全 向List<DateTime>实例添加一 ...

  6. <NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型

    5.1 编程语言的基元类型 c#不管在什么操作系统上运行,int始终映射到System.Int32; long始终映射到System.Int64 可以通过checked/unchecked操作符/语句 ...

  7. <NET CLR via c# 第4版>笔记 第6章 类型和成员基础

    6.1 类型的各种成员 6.2 类型的可见性 public 全部可见 internal 程序集内可见(如忽略,默认为internal) 可通过设定友元程序集,允许其它程序集访问该程序集中的所有inte ...

  8. <NET CLR via c# 第4版>笔记 第7章 常量和字段

    7.1 常量 常量 是值从不变化的符号.定义常量符号时,它的值必须能够在编译时确定. 只能定义编译器识别的基元类型的常量,如果是非基元类型,需把值设为null. 常量的值直接嵌入代码,所以不能获取常量 ...

  9. <NET CLR via c# 第4版>笔记 第8章 方法

    8.1 实例构造器和类(引用类型) 构造引用类型的对象时,在调用类型的实例构造器之前,为对象分配的内存总是先被归零 .没有被构造器显式重写的所有字段都保证获得 0 或 null 值. 构造器不能被继承 ...

随机推荐

  1. 51nod 1413 权势二进制

    本来刚开始还是想用每一位 -1的个数 然后再乘以10  不断累加 后来发现 完全不是这回事啊  因为本身就是0 和 1 所以只要记录出现的最大的数字 就是答案  因为 n >= 1 // 所以不 ...

  2. 论文笔记:Mastering the game of Go with deep neural networks and tree search

    Mastering the game of Go with deep neural networks and tree search Nature 2015  这是本人论文笔记系列第二篇 Nature ...

  3. 【Coursera】Security Introduction -Ninth Week(1)

    前言 Coursera 的 Internet History,Technology,and Security 进入最后一周的学习了,在这最后一周内,需要进行的内容是 public-key 公钥系统的讲 ...

  4. 【转载】可被路由的协议 & 路由协议 & 不可被路由的协议 的区别

    原文地址:可被路由的协议 & 路由协议 & 不可被路由的协议 的区别 术语routed protocol(可被路由的协议)和routing protocol(路由协议)经常被混淆.可被 ...

  5. 2018/7/18Bad English

    1 So instead of enjoying anime because it can portray interesting stories in a form that would be im ...

  6. -bash: brew: command not found

    ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ...

  7. ant的原理

    ANT批量完成项目代码的重新编译.打包.测试.java语言编写与平台无关的. Ant工具 Ant是一种基于Java的build工具.理论上来说,它有些类似于(Unix)C中的make ,但没有make ...

  8. xss脚本注入后端的防护

    1.脚本注入最主要的是不要相信用户输入的任何数据,对数据进行转义 可以使用:org.springframework.web.util.HtmlUtils包中的 HtmlUtils.htmlEscape ...

  9. Lua和C++交互 学习记录之一:C++嵌入脚本

    主要内容转载自:子龙山人博客(强烈建议去子龙山人博客完全学习一遍) 部分内容查阅自:<Lua 5.3  参考手册>中文版 译者 云风 制作 Kavcc vs2013+lua-5.3.3 1 ...

  10. 怎么运行cocos2dx 3.x simulator?

    1.simulator的好处是: 快速切换分辨率:F5快速重新启动项目: 这对于脚本语言来说都是很方便快捷的. 2.涉及到显示参数的文件有两个: ①lang,这个是菜单的语言文件,如果没有这个文件的话 ...