C# 不带“virtual”的基本方法与带“virtual”的基本方法以及带“new”的派生方法?

C# 不带“virtual”的基本方法与带“virtual”的基本方法以及带“new”的派生方法?,c#,C#,我正在尝试理解新的虚拟方法 以下两种方式的区别是什么: 在基类中定义一个不带virtual的方法,并在子类中定义一个具有相同名称和签名的方法 使用virtual在基类中定义方法,并在子类中定义具有相同名称和签名以及new的方法 如果它们不同,为什么在下面的示例中它们的行为相同 using System; public class Base { public void DoIt() { Console.WriteLine ("Base: DoIt"); }

我正在尝试理解新的虚拟方法

以下两种方式的区别是什么:

  • 在基类中定义一个不带
    virtual
    的方法,并在子类中定义一个具有相同名称和签名的方法

  • 使用
    virtual
    在基类中定义方法,并在子类中定义具有相同名称和签名以及
    new
    的方法

如果它们不同,为什么在下面的示例中它们的行为相同

using System;

public class Base
{
    public void DoIt()
    {
      Console.WriteLine ("Base: DoIt");
    }

    public virtual void DoItVirtual1()
    {
      Console.WriteLine ("Base: DoItVirtual1");
    }

    public virtual void DoItVirtual2()
    {
      Console.WriteLine ("Base: DoItVirtual2");
    }    

}

public class Derived : Base
{
    public void DoIt()
    {
      Console.WriteLine ("Derived: DoIt");
    }
    public override void DoItVirtual1()
    {
      Console.WriteLine ("Derived: DoItVirtual1");
    }
    public new void DoItVirtual2()
    {
      Console.WriteLine ("Derived: DoItVirtual2");
    }        
}

class MainClass {
  public static void Main (string[] args) {
    Base b = new Base();
    Derived d = new Derived();
    Base bd = new Derived();

    b.DoIt(); 
    d.DoIt(); 
    bd.DoIt();

    b.DoItVirtual1();
    d.DoItVirtual1();
    bd.DoItVirtual1();

    b.DoItVirtual2();
    d.DoItVirtual2();
    bd.DoItVirtual2();
  }
}
输出:

Base: DoIt
Derived: DoIt
Base: DoIt
Base: DoItVirtual1
Derived: DoItVirtual1
Derived: DoItVirtual1
Base: DoItVirtual2
Derived: DoItVirtual2
Base: DoItVirtual2

new
修饰符产生的功能与要删除它的功能相同,但会抑制编译器关于无意中隐藏基类方法的警告

通过使用new,您声明您知道该成员 它修改隐藏从基类继承的成员

virtual
关键字与此处的
new
修饰符无关。它只允许您覆盖派生类中的方法

此处的更多信息:

在派生类中使用“new”关键字与不使用“new”具有相同的效果,无论是否存在“virtual”关键字

“虚拟”与“覆盖”成对工作

“new”仅在派生类上下文中起作用

如果它们不同,为什么在下面的示例中它们的行为相同

using System;

public class Base
{
    public void DoIt()
    {
      Console.WriteLine ("Base: DoIt");
    }

    public virtual void DoItVirtual1()
    {
      Console.WriteLine ("Base: DoItVirtual1");
    }

    public virtual void DoItVirtual2()
    {
      Console.WriteLine ("Base: DoItVirtual2");
    }    

}

public class Derived : Base
{
    public void DoIt()
    {
      Console.WriteLine ("Derived: DoIt");
    }
    public override void DoItVirtual1()
    {
      Console.WriteLine ("Derived: DoItVirtual1");
    }
    public new void DoItVirtual2()
    {
      Console.WriteLine ("Derived: DoItVirtual2");
    }        
}

class MainClass {
  public static void Main (string[] args) {
    Base b = new Base();
    Derived d = new Derived();
    Base bd = new Derived();

    b.DoIt(); 
    d.DoIt(); 
    bd.DoIt();

    b.DoItVirtual1();
    d.DoItVirtual1();
    bd.DoItVirtual1();

    b.DoItVirtual2();
    d.DoItVirtual2();
    bd.DoItVirtual2();
  }
}
查看输出中的差异:

Base: DoItVirtual1
Derived: DoItVirtual1
Derived: DoItVirtual1

Base: DoItVirtual2
Derived: DoItVirtual2
Base: DoItVirtual2
在重写的虚方法中,强制转换回基类的派生实例的行为类似于派生实例。这就是所谓的多态性。
在第二个示例中,在使用new关键字的情况下,运行时无法将强制转换实例与派生类连接。

虚拟成员允许派生类替换实现,而新成员是将成员隐藏在具有相同名称的基类中的附加成员

class A
{
    public virtual int X() => 1;
}

class B : A
{
    public override int X() => 2;
}

class C : B
{
    public new virtual int X() => 3;
}

class D : C
{
    public sealed override int X() => 4;
}
试验


你是说
new
可以用同样的方式重新定义基本方法,而不管基本方法是否符合
virtual
?否。
new
override
之间有重要区别<代码>覆盖启用多态性,而
新建
不启用多态性。当使用
覆盖
时,调用的方法始终是运行时类型。使用
new
时,为了调用派生方法,静态类型(或编译器时间类型)必须是派生类。换句话说,如果您有
BaseClass obj=new-DerivedClass()obj.DoItVirtual2()
,然后它将执行基类方法。如果您有
DerivedClass obj=new-DerivedClass()并执行相同的操作,它将运行DerivedClass方法。您不需要“重新定义”基方法。您只是故意在派生类中使用新定义“隐藏”基方法。对不起,我可能误解了您的问题<代码>新建
可以使用,无论
虚拟
,是。但这与使用
覆盖
不同。正如@Jacek所说,它隐藏了基本方法,而不是重新定义它。我们中的一些人想知道为什么语言设计者选择包含
new
,因为大多数时候使用它意味着一种代码味道/一个有问题的架构/维护噩梦。此外,你永远不能调用操作(如基类中指定的)来自外部基类的虚拟成员。只有派生类才能调用base。$$$的可能重复项