C#-关键字用法virtual + override与new


204

在基本类型“ virtual”中声明一个方法,然后在子类型中使用“ override”关键字覆盖该方法,而不是在子类型中new声明匹配方法时简单地使用“ ”关键字,这有什么区别?


3
MSDN说:“使用new创建具有相同名称的新成员并使原始成员隐藏,同时override扩展了继承成员的实现”
AminM 2013年


Answers:


181

“ new”关键字不会被覆盖,它表示与基类方法无关的新方法。

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

如果您使用覆盖,它将显示为true。

(摘自Joseph Daigle的基本代码)

因此,如果您正在执行真正的多态性,则应始终覆盖。您需要使用“ new”的唯一地方是该方法与基类版本没有任何关系。


您应该修改您的代码,因为我已经将方法设为静态(对于使用“ new”关键字有效)。但是我认为使用实例方法更加清晰。
约瑟夫·戴格尔,

1
谢谢,我错过了“静态”部分。应该更多地关注未来
阿尔伯汀

1
请注意,这里的Foo test = new Bar()行非常关键,当您将Bar放入Foo变量中时,metods的new / override关键字确定调用哪个metod。
Thomas N

......所以这是完全一样的根本具有virtual在基地和override派生?为什么存在?即使没有代码,代码也仍然可以运行new-因此,纯粹是可读性吗?
Don Cheadle 2015年

亲爱的先生,您的回答很含糊。new和virtual-override做同样的事情,唯一的不同是new HIDES父类中的方法并覆盖了。当然,它会打印false,因为您的测试对象的类型为Foo,如果它的类型为Bar,则它的打印结果为true。不过,这个例子很棘手。
MrSilent 2015年

228

我总是发现通过图片更容易理解这样的事情:

同样,以约瑟夫·戴格尔的代码,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

如果您随后这样调用代码:

Foo a = new Bar();
a.DoSomething();

注意:重要的是我们的对象实际上是一个Bar,但是我们将其存储在类型变量中Foo(这与强制转换类似)。

然后,结果将如下所示,具体取决于您使用virtual/ override还是new声明类时。

虚拟/替代说明图


3
谢谢....但是您能不能对您所说的演员表解释一下上面的图片?
odiseh

糟糕,如果忘记将这几行添加到我的上一页中。评论:您是说虚拟/覆盖和非虚拟/新仅用于多态概念,而当您简单地声明一个变量(不使用强制转换)时,它们并不意味着?再次感谢。
odiseh

这个答案正是我想要的。我唯一的问题是flickr图像在我的工作场所被阻止,所以我只能通过电话访问它……
mezoid

7
真正努力回答这个问题。
iMatoria

如果您可以修复图像,我会投票赞成吗?它被阻止在公司防火墙后面...
Jeremy Thompson

43

以下是一些代码,用于了解虚拟方法和非虚拟方法的行为差异:

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

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

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

谢谢你-但是为什么new当根本不使用覆盖时,为什么使用“隐藏”基本方法呢?
Don Cheadle 2015年

1
我认为它不是为了防止基类被覆盖而创建的。我认为它是为了避免名称冲突而创建的,因为您无法覆盖不是该方法的方法,virtual如果编译器在没有virtual签名的类“
bloodline

19

new关键字实际上创建了一个仅在该特定类型上存在的全新成员。

例如

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

该方法在两种类型上都存在。当您使用反射并获取type的成员时Bar,您实际上会发现2种方法DoSomething()看起来完全相同。通过使用它,new您可以有效地将实现隐藏在基类中,这样,当类派生自Bar(在我的示例中)时,to的方法调用base.DoSomething()转到Barnot Foo


9

virtual / override告诉编译器这两个方法是相关的,并且在某些情况下,当您认为您正在调用第一个(虚拟)方法时,实际上调用第二个(重写)方法是正确的。这是多态性的基础。

(new SubClass() as BaseClass).VirtualFoo()

将调用子类的重写VirtualFoo()方法。

new告诉编译器您要向派生类中添加一个方法,该方法的名称与基类中的方法相同,但彼此之间没有关系。

(new SubClass() as BaseClass).NewBar()

将调用BaseClass的NewBar()方法,而:

(new SubClass()).NewBar()

将调用子类的NewBar()方法。


真的很喜欢这句话“告诉编译器”
Mina Gabriel

“多态性的基础”是另一个经典的说法:)
Jeremy Thompson

8

除了技术细节之外,我认为使用虚拟/覆盖可以传达设计上的许多语义信息。当您将方法声明为虚拟方法时,表示您希望实现类可能希望提供自己的非默认实现。同样,在基类中省略此方法也声明了以下期望:默认方法应足以满足所有实现的类。同样,可以使用抽象声明来强制实现类提供自己的实现。同样,我认为这传达了很多关于程序员期望代码使用方式的信息。如果我同时编写了基类和实现类,并且发现自己使用的是新的类,那么我将认真考虑是否不要在父级中使该方法虚拟化并明确声明我的意图的决定。


关于新vs覆盖的技术解释是正确的,但是这个答案似乎最能指导开发人员使用。
2014年

4

Override关键字和new关键字之间的区别在于,前者执行方法重写,而后者执行方法隐藏。

查看以下链接以获取更多信息...

MSDN其他


3
  • new关键字用于隐藏。-表示您在运行时隐藏了方法。输出将基于基类方法。
  • override覆盖。-表示您正在使用基类的引用来调用派生类的方法。输出将基于派生类方法。

1

我的解释版本来自使用属性来帮助理解差异。

override很简单吧?基础类型将覆盖父级的类型。

new也许是误导(对我而言)。使用属性更容易理解:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

使用调试器,您会注意到它Foo foo具有2个 GetSomething属性,因为它实际上具有2个版本的属性Foo“ s”和Bar“ s”,并且要知道要使用哪个版本,c#会“拾取”当前类型的属性。

如果要使用Bar的版本,则应使用替代或Foo foo替代。

Bar bar仅有1个,因为它想要的全新行为GetSomething


0

不使用任何方法标记方法意味着:使用对象的编译类型而不是运行时类型(静态绑定)来绑定此方法。

用方法标记方法virtual:使用对象的运行时类型而不是编译时间类型(动态绑定)绑定此方法。

virtualoverride派生类标记基类方法意味着:这是要使用对象的运行时类型进行绑定的方法(动态绑定)。

在派生类中标记具有一个基类virtual方法的方法new是:这是一个新方法,它与基类中具有相同名称的方法没有关系,并且应使用对象的编译时类型(静态绑定)进行绑定。

virtual在派生类中未标记基类方法意味着:该方法被标记为new(静态绑定)。

标记方法abstract意味着:该方法是虚拟的,但是我不会为其声明一个主体,并且它的类也是抽象的(动态绑定)。

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.