代码如下:
public class VirtualClass
{
public virtual void virtualMethod()
{
Console.WriteLine("in virtual");
}
public virtual void virtualMethod2() {
Console.WriteLine("sssssssss");
}
public void commonMethod() { }
}
public class DerivedClass : VirtualClass
{
public override void virtualMethod()
{
Console.WriteLine("sss");
}
public new void virtualMethod2()
{
base.virtualMethod2();
}
}
使用ILDASM查看一下编译后的程序集的元数据,virtualClass类中的三个方法的元数据如下所示:
Method #1 (0600000d)
-------------------------------------------------------
MethodName: virtualMethod (0600000D)
Flags : [Public] [Virtual] [HideBySig] [NewSlot] (000001c6)
RVA : 0x000020a0
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
Method #2 (0600000e)
-------------------------------------------------------
MethodName: virtualMethod2 (0600000E)
Flags : [Public] [Virtual] [HideBySig] [NewSlot] (000001c6)
RVA : 0x000020ae
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
Method #3 (0600000f)
-------------------------------------------------------
MethodName: commonMethod (0600000F)
Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
RVA : 0x000020bc
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
第一个方法virtualMethod的flags标记是重点,内容如下:
Flags : [Public] [Virtual] [HideBySig] [NewSlot] (000001c6)
可以发现,每当定义一个virtual方法,编译器就会在这个方法的元数据flag字段中,设置virtual,newslot标记。这里面newslot标记是重中之重,编译器会在该类的虚方法中新建一个一个槽,这样派生类就可以override他了。
做为对比,可以看一下commonMethod方法,可以发现他的flags里面既没有virtual,也没有newslot,这说明他不能享受viruta方法所带来的种种特殊待遇。
2.abstract方法
代码如下:
public abstract class AbstractClass
{
public abstract void abstractMethod1();
public abstract void abstractMethod2();
}
public class NonAbstract : AbstractClass
{
public override void abstractMethod1()
{
int a;
}
public override void abstractMethod2()
{
throw new NotImplementedException();
}
}
abstract中方法的元数据表如下所示:
Method #1 (06000005)
-------------------------------------------------------
MethodName: abstractMethod1 (06000005)
Flags : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract] (000005c6)
RVA : 0x00000000
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
Method #2 (06000006)
-------------------------------------------------------
MethodName: abstractMethod2 (06000006)
Flags : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract] (000005c6)
RVA : 0x00000000
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
与virtual方法不同的是,abstract方法的flags中又多了一个abstract标记,这个标记就告诉编译器,这个方法不应该有具体实现,这一点可以从RVA字段中可以看出,任何abstract方法的RVA都是0。这里可以发现,abstract方法除了多了一个abstract标记以外,与virtual方法没有任何区别。
3.接口方法
代码如下:
public interface interface1
{
void interface1method();
}
public class interfaceClass : interface1//, interface2
{
public void interface1method()
{
int a;
}
}
interface中方法的元数据如下:
Method #1 (06000001)
-------------------------------------------------------
MethodName: interface1method (06000001)
Flags : [Public] [Virtual] [HideBySig] [NewSlot] [Abstract] (000005c6)
RVA : 0x00000000
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
从这里可以看出,接口方法和抽象方法没有什么却别,都是virtual,abstract,newslog这三个标记。他们之间最主要的却别在于派生类的使显示上,下面来看一下interfaceClass类中方法的元数据,这个类实现了interface1接口。
Method #1 (06000003)
-------------------------------------------------------
MethodName: collectionThinking.interface1.interface1method (06000003)
Flags : [puglic] [Final] [Virtual] [HideBySig] [NewSlot] (000001e1)
RVA : 0x00002050
ImplFlags : [IL] [Managed] (00000000)
CallCnvntn: [DEFAULT]
hasThis
ReturnType: Void
No arguments.
这里我们发现,flags中多了一个final标记,这个标记告诉编译器,这个方法不能被override,也就是说我们不能写出如下的方法来:
public class secondLevalClass : interfaceClass
{
public override void interface1Method()
{}
}
如果写出这样的代码来,编译器会给出一个错误,提示没有找到合适的方法来重写interfaceMethod方法。这是因为interfaceClass中的interface1Method方法具有final标记。
然而实现一个抽象方法则不具有这个中限制,实现一个抽闲方法时,编译器不会加上final标记,所以派生类可以override基类中的方法。
总结:
不管是虚方法,接口方法还是抽象方法,都具有一个共同的特点:virtual和newslog标记。其中newslot尤其重要,有了这个标记,派生类就可以override了。
对于接口方法和抽象方法,有一个abstract标记,这个标记告诉编译器,这个方法不能有任何实现,并且类不能被实例化。
从一个接口中继承方法时,如果不显示指定virtual关键字,编译器会在最终生成的元数据中加上一个final标记,这样派生类就不能实现了他了。