您的位置:首页 > 编程语言 > C#

《CLR via C#》读书笔记(9)– 接口

2013-01-11 16:29 288 查看
接口就是将一些相关联的动作集合在一起形成一个契约,然后让类可以去实现这些契约。

接口中定义的都是动作(方法相关:比如属性,方法,事件)的签名,没有实现

接口中定义的成员都是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内部会去调用相应的方法


因为这一点,其实显示实现接口不太好,因为无法通过类的实例变量直接调用,未免会有点怪。

值类型实现接口
值类型也可以实现接口。但是由于接口变量是属于引用类型,将值类型隐式转换为接口变量时需要将值类型装箱。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: