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. github上的markdown如何换行

    https://gist.github.com/shaunlebron/746476e6e7a4d698b373 1.普通的换行 在文本结束后面,加2个空格 2.段落之间的换行 使用反斜杠\

  2. darknet-训练自己的yolov3模型

    目录 Yolo v3的使用方法 安装darknet 训练Pascal VOC格式的数据 修改cfg文件中的voc.data 修改VOC.names 下载预训练卷积层权重 修改cfg/yolov3-vo ...

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

    对于公钥系统,我们现在已经有了保证它 Confidentially 的一种方法:SSL.SSL利用了公钥的概念. 那么 who we are talking to? Integrity Certifi ...

  4. 【TCP/IP详解 卷一:协议】第一章概论 学习笔记

    第一章 概述 游览了一下,本章主要是简介OSI模型的低层(1-4层).介绍了TCP/IP协议族的相关知识. TCP/IP的分层 Application <--> TCP / UDP < ...

  5. lncRNA研究利器之"TANRIC"

    http://bioinformatics.mdanderson.org/main/Main_Page http://ibl.mdanderson.org/tanric/_design/basic/i ...

  6. 字典重复key的合并

    from collections import defaultdict d=defaultdict(set) s=[("001","A"),("001 ...

  7. Github客户端操作

    Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理.作为一个程序员,我们需要掌握其用法. 作为开源代码库以及版本控制系统,Github目前拥有140 ...

  8. zlib__ZC

    官网:http://www.zlib.net/ ,所有版本下载:http://www.zlib.net/fossils/ ZC: 我下载的是 zlib-1.2.3.tar.gz 和 zlib-1.2. ...

  9. [.NET开发] C# 合并、拆分PDF文档

    在整理文件时,将多个同类型文档合并是实现文档归类的有效方法,也便于文档管理或者文档传输.当然,也可以对一些比较大的文件进行拆分来获取自己想要的部分文档.可以任意地对文档进行合并.拆分无疑为我们了提供极 ...

  10. 新概念 Lesson 1 Excuse me!

    xu言: 从哪里跌倒,就从哪里爬起来.希望这次真的能够坚持下去... standard  ['stændəd]    pronunciation [prə,nʌnsɪ'eɪʃ(ə)n] basic   ...