在基类型"EDOCX1"〔0〕中声明方法,然后在子类型中使用"EDOCX1"〔1〕关键字重写该方法,与在子类型中声明匹配方法时仅使用"EDOCX1"〔2〕关键字有什么区别?
- msdn说"使用new创建一个同名的新成员并使原始成员隐藏,而override扩展了继承成员的实现"
- 请参阅stackoverflow.com/questions/17717570/…
- msdn-msdn.microsoft.com/en-us/library/ms173153.aspx
我总是觉得这样的事情用图片更容易理解:
再一次,用约瑟夫·戴格尔的密码,
1 2 3 4 5 6 7 8 9
| public class Foo
{
public /*virtual*/ bool DoSomething() { return false; }
}
public class Bar : Foo
{
public /*override or new*/ bool DoSomething() { return true; }
} |
如果您这样调用代码:
1 2
| Foo a = new Bar ();
a .DoSomething(); |
注意:重要的是我们的对象实际上是一个Bar,但是我们将它存储在一个Foo类型的变量中(这与强制转换类似)。
然后,根据您在声明类时是使用virtual/override还是new,结果如下。
- 谢谢……但是你能解释一下上面这张关于你说的演员阵容的图片吗?
- 哎呀,如果忘记把这几行加到我的前一行。注释:您的意思是虚拟/重写和非虚拟/新仅用于多形态的概念,当您简单地声明一个变量(不使用强制转换)时,它们并不意味着什么?再次感谢。
- 这个答案正是我想要的。我唯一的问题是Flickr的图片在我的工作场所被屏蔽了,所以我不得不通过手机访问它…
- 努力回答这个问题。
- 如果你能修复图像,我会投赞成票?它在公司防火墙后面被屏蔽了…
- @Jeremythompson我把它从Flickr移到了StackOverflow现在拥有的imgur驱动的主机上。希望你的公司防火墙不再阻止它(我们的Flickr也被阻止了)。同时,随着Flickr所有权/政策的改变,我觉得最好还是离开它。
"new"关键字不重写,它表示与基类方法无关的新方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 ());
}
} |
如果使用override,它将打印为true。
(基础代码取自Joseph Daigle)
因此,如果您正在执行真正的多态性,那么应该始终覆盖它。唯一需要使用"new"的地方是当方法与基类版本没有任何关联时。
- 你应该修改你的代码,我已经将方法设置为静态的(这对于使用"new"关键字是有效的)。但我认为使用实例方法更清晰。
- 谢谢,我错过了"静态"部分。应该更加关注未来
- 请注意,行foo test=new bar()在这里非常重要,metods的new/override关键字决定在将条放入foo变量时调用哪个metod。
- …所以这和在基部没有virtual和在派生中没有override完全一样?它为什么存在?即使不使用new,代码仍然可以运行,那么它仅仅是可读性吗?
- 亲爱的先生,你的回答很含糊。new和virtual override做的都差不多,唯一的区别是new将方法隐藏在父类和override中。好吧,超越它。当然,它打印错误,因为您的测试对象是foo类型,如果它是bar类型,它将打印为true。不过,这是一个相当棘手的例子。
- 成员字段声明中的new关键字对我来说没有多大意义。我仍然可以在函数体中调用EDOCX1[4]
下面是一些代码来理解虚拟方法和非虚拟方法的行为差异:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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;
}
} |
- 感谢您这样做——但是为什么在不使用override的情况下,使用new来"隐藏"基方法呢?
- 我不认为它是为了防止基类被重写而创建的。我认为它的创建是为了避免名称冲突,因为您不能重写一个不是virtual的方法,如果编译器在没有virtual签名的类"bloodline"上看到相同的函数名,它会抱怨。
new关键字实际上创建了一个只存在于该特定类型上的全新成员。
例如
1 2 3 4 5 6 7 8 9
| public class Foo
{
public bool DoSomething () { return false; }
}
public class Bar : Foo
{
public new bool DoSomething () { return true; }
} |
这两种类型上都存在该方法。当使用反射并获取Bar类型的成员时,实际上会发现两个名为DoSomething()的方法看起来完全相同。通过使用new可以有效地隐藏基类中的实现,这样当类从Bar派生时(在我的示例中),对base.DoSomething()的方法调用将转到Bar而不是Foo。
virtual/override告诉编译器这两个方法是相关的,并且在某些情况下,当您认为正在调用第一个(virtual)方法时,实际上调用第二个(override)方法是正确的。这是多态性的基础。
1
| (new SubClass () as BaseClass ).VirtualFoo() |
将调用子类的overriden virtualfoo()方法。
new告诉编译器您正在向派生类添加一个方法,该方法与基类中的方法同名,但它们之间没有关系。
1
| (new SubClass () as BaseClass ).NewBar() |
将调用基类的newbar()方法,但是:
1
| (new SubClass ()).NewBar() |
将调用子类的newbar()方法。
- 就像这个句子"告诉编译器"
- "多态性基础"是另一种经典说法:
除了技术细节之外,我认为使用虚拟/覆盖在设计上传递了很多语义信息。当您声明一个方法是虚拟的时,您表示您希望实现类可以提供它们自己的非默认实现。同样,在基类中省略这一点也声明了这样一种期望,即默认方法应该足以满足所有实现类。类似地,可以使用抽象声明强制实现类提供自己的实现。同样,我认为这传达了很多关于程序员期望如何使用代码的信息。如果我同时编写基类和实现类,发现自己在使用new,我会认真地重新考虑不在父类中使方法虚拟化的决定,并明确声明我的意图。
- 新的vs覆盖的技术解释是合理的,但是这个答案似乎对指导开发者使用是最有帮助的。
override关键字和new关键字的区别在于前者进行方法重写,后者进行方法隐藏。
有关详细信息,请查看以下链接…
msdn和其他
- new关键字用于隐藏。-意味着您正在运行时隐藏方法。输出将基于基类方法。
- override表示优先。-意味着您正在使用基类的引用调用派生类方法。输出将基于派生类方法。
我的解释版本来自于使用属性帮助理解差异。
override足够简单,对吗?基础类型重写父类型。
new可能是误导(对我来说是这样)。有了属性,更容易理解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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和Bar的属性,为了知道要使用哪一个,c"选择"当前类型的属性。
如果您想使用BAR的版本,您应该使用override或使用Foo foo。
Bar bar只有1个,因为它希望GetSomething有全新的行为。
不以任何方式标记方法:使用对象的编译类型而不是运行时类型(静态绑定)绑定此方法。
使用virtual标记方法意味着:使用对象的运行时类型绑定此方法,而不是编译时类型(动态绑定)。
在派生类中用override标记基类virtual方法意味着:这是使用对象的运行时类型(动态绑定)绑定的方法。
在派生类中用new标记一个基类virtual方法意味着:这是一个新方法,它与基类中同名的方法没有关系,应该使用对象的编译时类型(静态绑定)进行绑定。
在派生类中不标记基类virtual方法意味着:此方法被标记为new(静态绑定)。
标记一个方法abstract意味着:这个方法是虚拟的,但我不会为它声明主体,它的类也是抽象的(动态绑定)。