<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 值. 构造器不能被继承 ...
随机推荐
- svn的下载链接
想要下载svn结果网上出来都是tortoisesvn 正确的链接是 源代码 http://subversion.apache.org/ 安装包 http://www.collab.net/downlo ...
- LOJ#2632. 「BalticOI 2011 Day1」打开灯泡 Switch the Lamp On
题目描述 译自 BalticOI 2011 Day1 T3「Switch the Lamp On」有一种正方形的电路元件,在它的两组相对顶点中,有一组会用导线连接起来,另一组则不会.有 N×M 个这样 ...
- org.apache.shiro.session.InvalidSessionException: java.lang.IllegalStateException: getAttribute: Session already invalidated] with root cause
1.遇到以下异常,找了好长时间,终于解决,报的异常如下: 七月 07, 2017 3:02:16 下午 org.apache.catalina.core.StandardWrapperValve in ...
- 02_Flume1.6.0安装及单节点Agent实践
Flume1.6.0的安装1.上传Flume-1.6.0-tar.gz到待部署的所有机器 以我的为例: /usr/local/src/ 2.解压得到flume文件夹 # tar -x ...
- axios post请求报错
问题描述: vue中使用axios提交post请求, 请求地址及参数都对, 但是一直报缺少参数的错误 探索:对比post请求数据, 提交数据的方式不对 (1)axios的post请求(返回响应缺少参数 ...
- Java中的this
首先this作为关键字其实是随着对象的创建而产生的,当我们调用对象的一个方法的时候: 例如: A a = new A(); a.f(1) 其实我们可以理解为a.f(a,1) 编译器默默的把所操作的对 ...
- Jmeter 测试API接口 查看接口的幂等问题
背景介绍: 比如一个注册接口,要求填入的手机号与DB中已有的不能重复, 如果手机号码重复,则此次注册失败,不会新增会员数据: 如果不重复,则注册成功(忽略其他因素). 但是用20个并发,同样的请求,请 ...
- 使用 data.table 包操作数据
在第一节中,我们回顾了许多用于操作数据框的内置函数.然后,了解了 sqldf 扩展包,它使得简单的数据查询和统计变得更简便.然而,两种方法都有各自的局限性.使用内置函数可能既繁琐又缓慢,而相对于各式各 ...
- 快速幂模n运算
模运算里的求幂运算,比如 5^596 mod 1234, 当然,直接使用暴力循环也未尝不可,在书上看到一个快速模幂算法 大概思路是,a^b mod n ,先将b转换成二进制,然后从最高位开始(最高位一 ...
- Beta冲刺
第一天 日期:2018/6/24 1 今日完成任务情况. 妥志福.牛瑞鑫: 完成任务:数据库设计完成数据导入成功 王胜海.马中林: 完成任务:代码规范检查 董润园.邓英蓉: 完成任务:平台基本功能黑盒 ...