代码改变世界

从IL代码来探讨C#中的接口方法、虚方法与抽象方法

2012-08-03 17:18  Johnny Qian  阅读(2208)  评论(1编辑  收藏  举报

虚方法与抽象方法的比较是老生常谈的话题了。这次连带上接口方法来次深入探讨,从IL代码的层面来找出这些概念的不同之处。 首先贴上三种方法的C#与IL代码:

接口方法

C# Code:

interface ITest
{
    void Add(string s);
}

IL Code:

.class private interface abstract auto ansi ITest
{
    .method public hidebysig newslot abstract virtual instance void Add(string s) cil managed
    {
    }
}

虚方法

C# Code:

class Test
{
    public virtual void Add(string s)
    {
    }
}

IL Code:

.class private auto ansi beforefieldinit Test
    extends [mscorlib]System.Object
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

    .method public hidebysig newslot virtual instance void Add(string s) cil managed
    {
    }
}

抽象方法

C# Code:

abstract class ATest
{
    public abstract void Add(string s);
}

IL Code:

.class private abstract auto ansi beforefieldinit ATest
    extends [mscorlib]System.Object
{
    .method family hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

    .method public hidebysig newslot abstract virtual instance void Add(string s) cil managed
    {
    }
}

三种方法的关键字对比

三种方法的声明中都有virtual关键字,这正体现了.Net中的三种多态:

  1. 接口多态
  2. 继承多态
  3. 抽象多态

除此之外,接口的方法还加上了abstract关键字,这表明了接口方法和抽象方法一样也不能声明函数体。 有了Virtual关键字,后代便可以通过override关键字来进行重写。于是我们来看看这三种方法重写后的方法在IL代码上有什么不同。

实现接口的类和方法

C# Code:

class A :ITest
{
    public void Add(string s)
    {
    }
}

IL Code:

.class private auto ansi beforefieldinit A
    extends [mscorlib]System.Object
    implements InterfaceTest.ITest
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

    .method public hidebysig newslot virtual final instance void Add(string s) cil managed
    {
    }
}

重写虚方法的类和方法

C# Code:

class B:Test
{
    public override void Add(string s)
    {
    }
}

IL Code:

.class private auto ansi beforefieldinit B
    extends InterfaceTest.Test
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

    .method public hidebysig virtual instance void Add(string s) cil managed
    {
    }
}

继承抽象方法的类和方法

C# Code:

class C:ATest
{
    public override void Add(string s)
    {
    }
}

IL Code:

.class private auto ansi beforefieldinit C
    extends InterfaceTest.ATest
{
    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
    {
    }

    .method public hidebysig virtual instance void Add(string s) cil managed
    {
    }
}

结论

  1. 接口方法隐式声明为 abstract virtual
  2. 抽象方法隐式声明为 virtual
  3. 接口的实现方法 被声明为 virtual final,且在重写时不需要添加关键字 override
  4. 对于CLR而言,虚方法与抽象方法是一样的
  5. 重写虚方法和抽象方法,均要添加关键字 override
  6. 由于实现接口的类的方法被标记为 virtual final,因此该类的后代不能再重写该接口方法了(即不需要再次实现祖先的接口)。
  7. 重写了虚方法和抽象方法的类的后代依然可以重写祖先的虚方法和抽象方法,因为重写后的方法仍被标记为 virtual。如果要阻止它的子孙类继续重写该虚方法或抽象方法,可在重写方法时加上关键字sealed,这样该重写方法被声明为 virtual final,和实现了接口的方法声明一样了,因此可以称接口方法为单层的虚方法。

有不对的地方,欢迎大家拍砖。