《CLR via C#》读书笔记(9)– 接口
2013-01-11 16:29
288 查看
接口就是将一些相关联的动作集合在一起形成一个契约,然后让类可以去实现这些契约。
接口中定义的都是动作(方法相关:比如属性,方法,事件)的签名,没有实现
接口中定义的成员都是Public的(因为是对外公布的契约)
接口的名字一般用I开头,以标识其为接口
而对于实现了特定接口的类
必须实现接口中定义的所有动作
内存模型
看一下当类实现了一个接口时,CLR内部的内存布局是怎么样的。以如下实例为例:
在运行时,CLR中会生成如下数据结构:
调用模型
这样子的话如果我作如下调用:
CLR是会去上图中方法表中找到Dispose方法的代码地址执行。
但是如果作如下调用呢:
根据CLR的方法调用规则,如果方法没有定义为virtual,则直接在变量类型的类型对象方法表中查找代码地址。但是此处变量类型是一个接口,它不会保存Dispose方法的代码地址。
因此实际情况是C#编译器会将所在接口中定义的方法标记为virtual,这样的话在作上述调用时,CLR会去obj变量的真实类型的类型对象查找方法表。查找IDisposable.Dispose方法的代码地址。
通过IL代码验证一下:
源代码:
Dispose方法IL代码:
隐式接口实现与显示接口实现
一般情况下我们最直接的实现一个接口的方式是:直接实现一些与接口中定义的方法签名相同的方法。这种方式称为隐式实现。
还有一种方式是显示实现。何为显示实现呢,先来一睹为快。
上述代码在运行时,内存中的类型对象方法表中会保存三个Dispose方法的记录,并且三条记录的代码地址会不一样。因为在类实现中显示实现了两个接口中的Dispose方法。
注意:由于编译器将显示接口实现的方法标记为private,因此无法通过MessageHandler变量直接调用到这些方法。(private方法当然调不到啦)。
但是我们可以这样来调用:
因为这一点,其实显示实现接口不太好,因为无法通过类的实例变量直接调用,未免会有点怪。
值类型实现接口
值类型也可以实现接口。但是由于接口变量是属于引用类型,将值类型隐式转换为接口变量时需要将值类型装箱。
接口中定义的都是动作(方法相关:比如属性,方法,事件)的签名,没有实现
接口中定义的成员都是Public的(因为是对外公布的契约)
接口的名字一般用I开头,以标识其为接口
而对于实现了特定接口的类
必须实现接口中定义的所有动作
内存模型
看一下当类实现了一个接口时,CLR内部的内存布局是怎么样的。以如下实例为例:
在运行时,CLR中会生成如下数据结构:
调用模型
这样子的话如果我作如下调用:
MessageHandler messageHandler = new MessageHandler(); messageHandler.Dispose();
CLR是会去上图中方法表中找到Dispose方法的代码地址执行。
但是如果作如下调用呢:
IDisposable obj = messageHandler; obj.Dispose();
根据CLR的方法调用规则,如果方法没有定义为virtual,则直接在变量类型的类型对象方法表中查找代码地址。但是此处变量类型是一个接口,它不会保存Dispose方法的代码地址。
因此实际情况是C#编译器会将所在接口中定义的方法标记为virtual,这样的话在作上述调用时,CLR会去obj变量的真实类型的类型对象查找方法表。查找IDisposable.Dispose方法的代码地址。
通过IL代码验证一下:
源代码:
public class MessageHandler:IDisposable { public void Dispose() { throw new NotImplementedException(); } }
Dispose方法IL代码:
.method public hidebysig newslot virtual final //由于类定义中没有定义为virtual,编译器为方法添加了virtual final标记,确保方法不会被派生类重写 instance void Dispose() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: nop IL_0001: newobj instance void [mscorlib]System.NotImplementedException::.ctor() IL_0006: throw } // end of method MessageHandler::Dispose
隐式接口实现与显示接口实现
一般情况下我们最直接的实现一个接口的方式是:直接实现一些与接口中定义的方法签名相同的方法。这种方式称为隐式实现。
还有一种方式是显示实现。何为显示实现呢,先来一睹为快。
public interface ICustomDisposable { void Dispose(); } public class MessageHandler:IDisposable,ICustomDisposable {
// 注意:此处不能标记访问权限,因为编译器为将所有显示接口实现的方法标记为private.
void IDisposable.Dispose() { // Implementation. } void ICustomDisposable.Dispose() { // Implementation. } public void Dispose() { throw new NotImplementedException(); } }
上述代码在运行时,内存中的类型对象方法表中会保存三个Dispose方法的记录,并且三条记录的代码地址会不一样。因为在类实现中显示实现了两个接口中的Dispose方法。
注意:由于编译器将显示接口实现的方法标记为private,因此无法通过MessageHandler变量直接调用到这些方法。(private方法当然调不到啦)。
但是我们可以这样来调用:
IDisposable obj = messageHandler; obj.Dispose(); //CLR内部会去调用相应的方法
因为这一点,其实显示实现接口不太好,因为无法通过类的实例变量直接调用,未免会有点怪。
值类型实现接口
值类型也可以实现接口。但是由于接口变量是属于引用类型,将值类型隐式转换为接口变量时需要将值类型装箱。
相关文章推荐
- 《CLR via C#》读书笔记 之 接口
- 接口(CLR via C# 读书笔记)
- C#夯实基础之接口(《CLR via C#》读书笔记)
- [读书笔记]参数out和ref-CLR via C# edition3
- 《CLR via C#》读书笔记 之 计算限制的异步操作
- 《CLR via C#》读书笔记(6)类型和成员基础
- 《CLR via C#》读书笔记(6) -- 操作符重载
- <NET CLR via c# 第4版>笔记 第13章 接口
- CLR via C# 3 读书笔记(14):第2章 生成、打包、部署和管理应用程序与类型 — 2.5 程序集版本资源信息
- 《CLR via C#》读书笔记
- CLR via C# 读书笔记 5-4 在使用非托管资源情况下的GC
- CLR via C# 读书笔记2-7
- CLR via C# 读书笔记(五)关于“out”和“ref”关键字
- 属性——CLR via C# 读书笔记
- CLR via C# 读书笔记(七)线程同步的几种解决办法
- 《CLR via C#》读书笔记---09 参数
- 《CLR via C#》读书笔记(10)– 我不知道的String
- CLR via C# 读书笔记 1-3 前台线程和后台线程
- CLR.via.C#第三版 读书笔记
- CLR via C# 读书笔记 2-3 Cache Lines and False Sharing(高速缓冲区和错误共享???)