在基类型“”中声明一个方法,virtual
然后在子类型中使用“”关键字覆盖它,而不是在子类型中声明匹配方法时override
简单地使用“ ”关键字有什么区别?new
11 回答
我总是发现用图片更容易理解这样的事情:
再次,采用约瑟夫戴格尔的代码,
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();
注意:重要的是我们的对象实际上是 a Bar
,但是我们将它存储在一个类型的变量中Foo
(这类似于强制转换它)
然后结果将如下所示,具体取决于您是否使用virtual
/override
或new
在声明您的类时。
“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 ());
}
}
这将打印为 false,如果您使用覆盖,它将打印为 true。
(取自 Joseph Daigle 的基本代码)
所以,如果你在做真正的多态,你应该总是 OVERRIDE。唯一需要使用“new”的地方是该方法与基类版本没有任何关系。
下面是一些代码来理解虚拟和非虚拟方法的行为差异:
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
关键字实际上创建了一个仅存在于该特定类型上的全新成员。
例如
public class Foo
{
public bool DoSomething() { return false; }
}
public class Bar : Foo
{
public new bool DoSomething() { return true; }
}
该方法存在于两种类型上。当您使用反射并获取 type 的成员时Bar
,您实际上会发现 2 个调用DoSomething()
的方法看起来完全一样。通过使用new
,您可以有效地隐藏基类中的实现,以便当类从Bar
(在我的示例中)派生时,方法调用base.DoSomething()
转到Bar
而不是Foo
。
除了技术细节之外,我认为使用 virtual/override 可以在设计上传达很多语义信息。当您声明一个虚拟方法时,您表明您希望实现类可能希望提供它们自己的非默认实现。同样,在基类中省略它,声明了默认方法应该足以满足所有实现类的期望。类似地,可以使用抽象声明来强制实现类提供它们自己的实现。同样,我认为这传达了很多关于程序员期望如何使用代码的信息。如果我同时编写基类和实现类并且发现自己使用 new 我会认真重新考虑不在父类中使方法为虚拟的决定并明确声明我的意图。
virtual / override告诉编译器这两个方法是相关的,并且在某些情况下,当您认为您正在调用第一个(虚拟)方法时,实际上调用第二个(覆盖)方法是正确的。这是多态性的基础。
(new SubClass() as BaseClass).VirtualFoo()
将调用子类的重写 VirtualFoo() 方法。
new告诉编译器您正在向派生类添加一个与基类中的方法同名的方法,但它们彼此没有关系。
(new SubClass() as BaseClass).NewBar()
将调用 BaseClass 的 NewBar() 方法,而:
(new SubClass()).NewBar()
将调用子类的 NewBar() 方法。
new
关键字用于隐藏。- 表示您在运行时隐藏您的方法。输出将基于基类方法。override
为压倒一切。- 表示您正在使用基类的引用调用派生类方法。输出将基于派生类方法。
我的解释版本来自使用属性来帮助理解差异。
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 的版本,你会使用 override 或 useFoo foo
代替。
Bar bar
只有1,因为它想要全新的行为GetSomething
。
不使用任何方法标记方法意味着:使用对象的编译类型绑定此方法,而不是运行时类型(静态绑定)。
使用方法标记方法virtual
:使用对象的运行时类型绑定此方法,而不是编译时类型(动态绑定)。
在派生类中标记基类virtual
方法override
意味着:这是使用对象的运行时类型绑定的方法(动态绑定)。
在派生类中标记基类virtual
方法new
意味着:这是一个新方法,与基类中的同名方法没有关系,应该使用对象的编译时类型(静态绑定)进行绑定。
在派生类中不标记基类virtual
方法意味着:这个方法被标记为new
(静态绑定)。
标记一个方法abstract
意味着:这个方法是虚拟的,但我不会为它声明一个主体,它的类也是抽象的(动态绑定)。
using System;
using System.Text;
namespace OverrideAndNew
{
class Program
{
static void Main(string[] args)
{
BaseClass bc = new BaseClass();
DerivedClass dc = new DerivedClass();
BaseClass bcdc = new DerivedClass();
// The following two calls do what you would expect. They call
// the methods that are defined in BaseClass.
bc.Method1();
bc.Method2();
// Output:
// Base - Method1
// Base - Method2
// The following two calls do what you would expect. They call
// the methods that are defined in DerivedClass.
dc.Method1();
dc.Method2();
// Output:
// Derived - Method1
// Derived - Method2
// The following two calls produce different results, depending
// on whether override (Method1) or new (Method2) is used.
bcdc.Method1();
bcdc.Method2();
// Output:
// Derived - Method1
// Base - Method2
}
}
class BaseClass
{
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
public virtual void Method2()
{
Console.WriteLine("Base - Method2");
}
}
class DerivedClass : BaseClass
{
public override void Method1()
{
Console.WriteLine("Derived - Method1");
}
public new void Method2()
{
Console.WriteLine("Derived - Method2");
}
}
}