新和替代之间的区别


198

想知道以下内容之间有什么区别:

情况1:基类

public void DoIt();

情况1:继承的类

public new void DoIt();

情况2:基类

public virtual void DoIt();

情况2:继承的类

public override void DoIt();

根据我运行的测试,案例1和案例2似乎具有相同的效果。有区别还是首选方式?


2
重复的许多问题,包括stackoverflow.com/questions/159978/...
乔恩斯基特

Answers:


267

覆盖修饰符可用于虚拟方法,并且必须用于抽象方法。这指示编译器使用方法的最后定义的实现。即使该方法是在对基类的引用上调用的,它也将使用覆盖它的实现。

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

将调用Derived.DoIt如果覆盖Base.DoIt

new修饰符指示编译器使用子类实现而不是父类实现。任何不引用您的类但父类的代码都将使用父类实现。

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

先打个电话Base.DoIt,然后Derived.DoIt。实际上,它们是两个完全独立的方法,它们恰好具有相同的名称,而不是派生的方法覆盖基本方法。

资料来源:Microsoft网志


5
This indicates for the compiler to use the last defined implementation of a method。如何找到方法的最后定义的实现?
AminM 2014年

5
从一个具体的类开始,检查它是否具有所关注方法的实现。如果是这样,那么您就完成了。如果不是,请在继承层次结构中上一步,即检查超类是否具有感兴趣的方法。继续直到找到感兴趣的方法。
2014年

2
另请注意,只有override当基类将方法定义为时,您才可以使用方法virtual。这个词virtual是基类,上面写着:“嘿,当我调用此方法时,它实际上可能已被派生的实现所替代,所以我事先并不真正知道我在运行时实际上在调用哪种方法。因此,virtual表明–一个方法的占位符,这意味着未标记为的virtual方法不能被覆盖,但是您可以修饰符替换派生类中的任何非虚拟方法new,只能在派生级别访问
Erik Bongers,2005年

177

virtual:指示某个方法可以被继承者覆盖

覆盖:覆盖基类中虚拟方法的功能,提供不同的功能。

new隐藏原始方法(不必是虚拟的),提供不同的功能。仅应在绝对必要的情况下使用。

隐藏方法时,仍然可以通过强制转换为基类来访问原始方法。这在某些情况下很有用,但很危险。


2
为什么强制转换隐藏基本方法的方法很危险?或者,您是否暗示一般进行铸造很危险?
2016年

3
@Mark-调用者可能不知道实现,从而导致意外的误用。
Jon B

是否可以在父方法上使用override和/或newvirtual使用父方法?
亚伦·弗兰克

16

在第一种情况下,您将定义隐藏在父类中。这意味着仅当您将对象作为子类处理时才调用它。如果将类强制转换为其父类型,则将调用父方法。在第二种情况下,该方法将被覆盖,并且无论对象是否被强制转换为子类或父类,都将调用该方法。


7

尝试以下操作:(案例1)

((BaseClass)(new InheritedClass())).DoIt()

编辑:virtual + override在运行时解决(因此,override确实会覆盖虚拟方法),而new只是使用相同的名称创建新方法,并隐藏旧方法,则在编译时解决->您的编译器将其称为“看


3

在情况1中,如果您在将类型声明为基类时调用了继承类的DoIt()方法,则您甚至会看到基类的操作。

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

您能张贴您收到的警告或错误吗?当我最初发布该代码时,它运行良好。
Matthew Whited

所有这些都应该粘贴在您的入口点类(程序)中。删除该内容是为了更好地格式化此网站。
Matthew Whited

3

两种情况之间的区别在于,在情况1中,基本DoIt方法不会被覆盖,只会被隐藏。这意味着取决于变量的类型取决于将调用哪种方法。例如:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

这确实会造成混乱,并导致无法预期的行为,应尽可能避免。因此,首选方法是情况2。


3
  • new表示尊重您的REFERENCE类型()的左侧=,从而运行引用类型的方法。如果重新定义的方法没有new关键字,则其行为与原来相同。此外,它也称为非多态继承。也就是说,“我正在派生类中创建一个全新的方法,该方法与基类中同名的任何方法都完全没有关系。” -惠特克(Whitaker)说
  • override,必须virtual在其基类中与关键字一起使用,表示要尊重您的OBJECT类型(的右侧=),从而无论引用类型如何,都以覆盖的方式运行方法。此外,它也称为多态继承

我要记住两个关键字彼此相反的方式。

overridevirtual必须定义关键字以覆盖该方法。使用override关键字的方法,无论引用类型(基类的引用还是派生类的引用)都被基类实例化,该方法都将运行基类的方法。否则,将运行派生类的方法。

new:如果关键字是由方法使用的,则与override关键字不同,引用类型很重要。如果使用派生类实例化它,并且引用类型是基类,则运行基类的方法。如果使用派生类实例化它,并且引用类型是派生类,则运行派生类的方法。即,是override关键词的对比。顺便说一句,如果您忘记或忽略了向该方法添加新关键字,则默认情况下,编译器的行为与使用new关键字相同。

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

致电主:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

输出:

A
B
B
base test
derived test

新的代码示例,

通过一对一注释来播放代码。

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

如果override在派生类中使用关键字,则它会覆盖父方法。

如果new在派生类中使用了关键字,则派生方法被父方法隐藏。


1

我有同样的问题,这确实令人困惑,您应该考虑覆盖关键字仅适用于基类类型和派生类值的对象。在这种情况下,您只会看到override和new的效果:因此,如果您拥有class Aand B,并且B继承自A,那么您将实例化这样的对象:

A a = new B();

现在,调用方法将考虑其状态。 覆盖:意味着它扩展了方法的功能,然后在派生类中使用该方法,而new告诉编译器在派生类中隐藏该方法,而在基类中使用该方法。这是一个很好的视野:

https://msdn.microsoft.com/zh-CN/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

下面的文章在vb.net中,但是我认为关于新vs覆盖的解释非常容易理解。

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

在本文的某些时候,有这样的句子:

通常,Shadows假定调用了与该类型关联的功能,而Overrides假定执行了对象实现。

这个问题的公认答案是完美的,但是我认为本文提供了很好的示例,可以为这两个关键字之间的差异添加更好的含义。


1

在所有这些之中,是最令人困惑的。通过实验,new关键字就像为开发人员提供了通过显式定义类型来用基类实现覆盖继承类实现的选项。就像在想另一种方式。

在下面的示例中,结果将返回“派生结果”,直到将类型显式定义为BaseClass test,然后才返回“基础结果”。

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
如果您反对,请添加您的评论。奔跑是如此胆小。
有用的蜜蜂

0

这些测试中不会显示功能上的差异:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

在此示例中,所调用的Doit是您希望调用的Doit。

为了查看区别,您必须执行以下操作:

BaseClass obj = new DerivedClass();

obj.DoIt();

您将看到是否运行该测试,即在情况1(定义时)DoIt()BaseClass调用in,在情况2(定义时)DoIt()DerivedClass调用in 。


-1

在第一种情况下,它将调用派生类DoIt()方法,因为new关键字隐藏了基类DoIt()方法。

在第二种情况下,它将调用重写的DoIt()

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

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

让我们创建这些类的实例

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

一切都在上面。让将instanceB和instanceC设置为instanceA并调用DoIt()方法并检查结果。

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt(); 会给您C :: DoIt(),而不是B :: DoIt()
BYS2

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.