C#Virtual和Override的内部工作原理

hen*_*000 9 c# virtual inheritance overriding

关于C#虚拟和覆盖机制如何在内部工作的主题已经在程序员中讨论过死亡......但是在google上半小时之后,我找不到以下问题的答案(见下文):

使用简单的代码:

public class BaseClass
{
  public virtual SayNo() { return "NO!!!"; }
}

public class SecondClass: BaseClass
{
  public override SayNo() { return "No."; }
}

public class ThirdClass: SecondClass
{
  public override SayNo() { return "No..."; }
}

class Program
{
  static void Main()
  {
     ThirdClass thirdclass = new ThirdClass();
     string a = thirdclass.SayNo(); // this would return "No..."

     // Question: 
     // Is there a way, not using the "new" keyword and/or the "hide"
     // mechansim (i.e. not modifying the 3 classes above), can we somehow return
     // a string from the SecondClass or even the BaseClass only using the 
     // variable "third"?

     // I know the lines below won't get me to "NO!!!"
     BaseClass bc = (BaseClass)thirdclass;
     string b = bc.SayNo(); // this gives me "No..." but how to I get to "NO!!!"?
  }
}
Run Code Online (Sandbox Code Playgroud)

我想我不能简单地使用最派生的实例(不修改3个类的方法签名)来获取基类或中间派生类的方法.但我想证实并巩固我的理解......

谢谢.

Kon*_*lph 14

C#不能做到这一点,但它实际运用中可能的IL call代替callvirt.因此,您可以Reflection.Emit结合使用来解决C#的限制DynamicMethod.

这是一个非常简单的例子来说明它是如何工作的.如果你真的打算使用它,将它包装在一个很好的函数中,努力使它适用于不同的委托类型.

delegate string SayNoDelegate(BaseClass instance);

static void Main() {
    BaseClass target = new SecondClass();

    var method_args = new Type[] { typeof(BaseClass) };
    var pull = new DynamicMethod("pull", typeof(string), method_args);
    var method = typeof(BaseClass).GetMethod("SayNo", new Type[] {});
    var ilgen = pull.GetILGenerator();
    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.EmitCall(OpCodes.Call, method, null);
    ilgen.Emit(OpCodes.Ret);

    var call = (SayNoDelegate)pull.CreateDelegate(typeof(SayNoDelegate));
    Console.WriteLine("callvirt, in C#: {0}", target.SayNo());
    Console.WriteLine("call, in IL: {0}", call(target));
}
Run Code Online (Sandbox Code Playgroud)

打印:

callvirt, in C#: No.
call, in IL: NO!!!
Run Code Online (Sandbox Code Playgroud)


Jar*_*Par 7

如果不对样品进行修改并对折射进行折扣,那就没有办法了.虚拟系统的目的是强制调用派生的最大值,无论什么,CLR擅长其工作.

有几种方法可以解决这个问题.

选项1:您可以将以下方法添加到ThirdClass

public void SayNoBase() {
  base.SayNo();
}
Run Code Online (Sandbox Code Playgroud)

这将强制调用SecondClass.SayNo

选项2:这里的主要问题是您想要非虚拟地调用虚拟方法.C#只提供了一种通过基本修饰符完成此操作的方法.这使得无法以非虚方式调用自己类中的方法.您可以通过将其分解为第二种方法和代理来解决此问题.

public overrides void SayNo() {
  SayNoHelper();
}

public void SayNoHelper() {
  Console.WriteLine("No");
}
Run Code Online (Sandbox Code Playgroud)


gro*_*ver 1

您无法访问覆盖的基本方法。无论您如何转换对象,始终使用实例中的最后一个覆盖。