接口一个被我忽略的地方--接口重定向技术
习惯于用IDE生成接口方法了,右键点击"Implement Interface",生成所有的接口方法声明,还带个Region多方便啊.
今天看<<CLR Vir C#>>时才了解到自己是知其然不知其所有然啊.
实现接口方法很简单,新手估计也都会,但怎么理解这个过程,不见得所有人都知道
首先我们可以简单的认为Base类里有个Dispose()的方法.这个方法跟是不是IDisposable的成员,先理解为没有关系.当编译这个类的时候,编译器发现它实现了IDisposable接口,于是查找有没有跟其成员IDisposable.Dispose()同名,参数和返回类型完全匹配,并且是public的,如果找到就在IVMap里指向这个方法的入口(我猜的,这地方具体见http://www.microsoft.com/china/MSDN/library/netFramework/netframework/JITCompiler.mspx?mfr=true)如果没有就出个编译错误.
如果找到的方法前面有virtual关键字,编译器仍然认为是匹配的.如何没有virtual关键字,编译器默认的还会为其加上sealed.那么其子类是无法override的.如何想要override父类的Dispose()方法,最好是在Base中定义Dispose()时使用virtual.当然如果我们没有权利改写Base类,我们可以让其子类同样继承自IDisposable,然后改写方法.这点已经有很多人谈论过了,估计大家基本都明白.不了解的可以gongle it!
现在问题来了,我们是真的改写了父类的Dispose()方法吗?
测试一下:
Base c = new Derived();
c.Dispose();
结果你可以发现输出的是: Base's Dispose.当时我觉得很奇怪,后来静下来想想原来可以这么理解.
就如我开始理解接口实现过程一样,对于上面的代码我们可以直接忽略接口的存在,等效如下
这样大家应该都知道为什么输出是Base's Dispose,如果不知道可以去查看 "今天你多态了吗"一文,说的很详细了.
回过来看我们刚才的代码,编译的时候有个警告: 'Program.Derived.Dispose()' hides inherited member 'Program.Base.Dispose()'. Use the new keyword if hiding was intended.
是不是把Dispose()看成只是类的方法更容易理解呢.只是通过接口调用的时候指向了这个方法的入口而已.
我以为如下实现Derived类会更好些,不会让你产生误解,以为Derived override了Base类的Dispose()方法
对于如下代码不要期待能输出Derived's Dispose结果.当然如果CallDispose()传入的参数类型是IDisposable,就当我没说过
static void Main(string[] args)
{
Derived c = new Derived();
CallDispose(c);
}
private static void CallDispose(Base b)
{
b.Dispose();
}
要不然你得这样
private static void CallDispose(Base b)
{
((IDispose))b.Dispose();
}
但是你会觉得很别扭.当然 CallDispose(Base b)定义成这样本身就是个不好的设计.
具体大家可以看看<<CLR Vir C#>>14章,我想对于你理解接口实现的整个过程会有莫大的帮助