<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最主要的问题:
- 没有文档解释类型具体如何实现一个EIMI方法,也没有"智能感知"支持.
- 值类型的实例在转换成接口时装箱.
- EIMI不能由派生类型调用
13.11 设计:基类还是接口
可定义接口,同时提供实现该接口的基类.
<NET CLR via c# 第4版>笔记 第13章 接口的更多相关文章
- <NET CLR via c# 第4版>笔记 第19章 可空值类型
System.Nullable<T> 是结构. 19.1 C# 对可空值类型的支持 C# 允许用问号表示法来声明可空值类型,如: Int32? x = 5; Int32? y = null ...
- <NET CLR via c# 第4版>笔记 第18章 定制特性
18.1 使用定制特性 FCL 中的几个常用定制特性. DllImport 特性应用于方法,告诉 CLR 该方法的实现位于指定 DLL 的非托管代码中. Serializable 特性应用于类型,告诉 ...
- <NET CLR via c# 第4版>笔记 第17章 委托
17.1 初识委托 .net 通过委托来提供回调函数机制. 委托确保回调方法是类型安全的. 委托允许顺序调用多个方法. 17.2 用委托回调静态方法 将方法绑定到委托时,C# 和 CLR 都允许引用类 ...
- <NET CLR via c# 第4版>笔记 第16章 数组
//创建一个一维数组 int[] myIntegers; //声明一个数组引用 myIntegers = new int[100]; //创建含有100个int的数组 //创建一个二维数组 doubl ...
- <NET CLR via c# 第4版>笔记 第12章 泛型
泛型优势: 源代码保护 使用泛型算法的开发人员不需要访问算法的源代码.(使用c++模板的泛型技术,算法的源代码必须提供给使用算法的用户) 类型安全 向List<DateTime>实例添加一 ...
- <NET CLR via c# 第4版>笔记 第5章 基元类型、引用类型和值类型
5.1 编程语言的基元类型 c#不管在什么操作系统上运行,int始终映射到System.Int32; long始终映射到System.Int64 可以通过checked/unchecked操作符/语句 ...
- <NET CLR via c# 第4版>笔记 第6章 类型和成员基础
6.1 类型的各种成员 6.2 类型的可见性 public 全部可见 internal 程序集内可见(如忽略,默认为internal) 可通过设定友元程序集,允许其它程序集访问该程序集中的所有inte ...
- <NET CLR via c# 第4版>笔记 第7章 常量和字段
7.1 常量 常量 是值从不变化的符号.定义常量符号时,它的值必须能够在编译时确定. 只能定义编译器识别的基元类型的常量,如果是非基元类型,需把值设为null. 常量的值直接嵌入代码,所以不能获取常量 ...
- <NET CLR via c# 第4版>笔记 第8章 方法
8.1 实例构造器和类(引用类型) 构造引用类型的对象时,在调用类型的实例构造器之前,为对象分配的内存总是先被归零 .没有被构造器显式重写的所有字段都保证获得 0 或 null 值. 构造器不能被继承 ...
随机推荐
- 【旧版本】Ubuntu 14.04 下 P416编译器 p4c的安装
注:此为2017年5月份的安装方法,最新的p4c安装方法见: Ubuntu14.04下 安装p4c 参考: p4c README Ubuntu 14.04 下 P4v16编译器 p4c的安装 系统要求 ...
- python argparse模块--转载
add_argument:读入命令行参数,该调用有多个参数 ArgumentParser.add_argument(name or flags…[, action][, nargs][, const] ...
- BZOJ 1758 【WC2010】 重建计划
题目链接:重建计划 这道题现在已经成为一道板子题了…… 这是个非常显然的0-1分数规划,可以二分答案之后树分治判定一下.注意树分治的时候如果使用单调队列,需要把所有儿子预先按最大深度排好序,否则会被扫 ...
- Linux——文件处理命令简单学习总结
linux中一共有三种用户: 1: 所有者u(User) 2: 所属组g(group) 3: 其他用户o(other) linux中权限一共分三种: 1: r read 读权限 2: w write ...
- stat用法:获取文件对应权限的数字
题目:文件属性为-rw-r--r-- 对应权限为644,如何使用命令获取权限对应的数字?? 举例如下: [linuxidc@localhost ~]$ ll -l-rw-r--r-- 1 linuxi ...
- 如何学好Java大数据并快速提升
Java作为一种最流行的,最热门的编程语言,可以说它无处不在,目前全球有着数十亿的设备正在运行着Java,很多服务器程序都是用Java编写,用以处理每天超过数以千万的数据.无论是手机软件.手机Java ...
- Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks论文理解
一.创新点和解决的问题 创新点 设计Region Proposal Networks[RPN],利用CNN卷积操作后的特征图生成region proposals,代替了Selective Search ...
- 雷林鹏分享:Ruby 安装 - Unix
Ruby 安装 - Unix 下面列出了在 Unix 机器上安装 Ruby 的步骤. 注意:在安装之前,请确保您有 root 权限. 下载最新版的 Ruby 压缩文件.请点击这里下载. 下载 Ruby ...
- 20170523xlVBA多条件分类求和一例
Public Sub NextSeven_CodeFrame() Application.ScreenUpdating = False Application.DisplayAlerts = Fals ...
- Rspec: everyday-rspec实操。FactoryBot预构件 (rspec-expectations gem 查看匹配器) 1-4章
总文档连接: RSpec.info/documentation/ 包括core, expectiation,rails , mock, 点击最新版本,然后右上角搜索class, method. 第3章 ...