<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 值. 构造器不能被继承 ...
随机推荐
- Avito Cool Challenge 2018
考挂了.. A - Definite Game 直接看代码吧. #include<cstdio> #include<cstring> #include<algorithm ...
- NS3 MyApp Class Reference
官方文档:MyApp 可以在下面的几个例子找到: examples/tutorial/fifth.cc examples/tutorial/seventh.cc examples/tutorial/s ...
- Web前端代码规范
新增:http://materliu.github.io/code-guide/#project-naming HTML 原则1.规范 .保证您的代码规范,保证结构表现行为相互分离.2.简洁.保证代码 ...
- CSU 1805 Three Capitals(矩阵树定理+Best定理)
http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1805 题意: A和B之间有a条边,A和G之间有b条边,B和G之间有c条边.现在从A点出发走遍所 ...
- AtCoder Grand Contest 013 C :Ants on a Circle
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- 华中农业大学预赛 B Handing Out Candies 余数的和
Problem B: Handing Out Candies Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 258 Solved: 19[Submit ...
- 《剑指offer》第二十八题(对称的二叉树)
// 面试题28:对称的二叉树 // 题目:请实现一个函数,用来判断一棵二叉树是不是对称的.如果一棵二叉树和 // 它的镜像一样,那么它是对称的. #include <iostream> ...
- Python string常用函数
2017-07-03 23:26:08 1..replace(self, old, new, count=-1) replace()函数将旧字符串替换为新字符串,最后一个参数count为可选项,表示替 ...
- 介绍 Jersey 依赖
Jersey 使用 Apache Maven 进行构造和安装.非快照(Non-snapshot)Jersey 发行版本是不会部署到 中心 Maven 仓库(Central Maven Reposito ...
- Service Account和其secrets 作用和场景,看了不亏。。
Service Account概念的引入是基于这样的使用场景: 运行在pod里的进程需要调用Kubernetes API以及非Kubernetes API的其它服务.Service Account它并 ...