2011-05-28 91 views
55

我很迷惑OOP的一些概念:virtual,override,newsealed override。任何人都可以解释不同之处虚拟,覆盖,新和密封覆盖之间的区别

我很清楚,如果要使用派生类方法,可以使用override关键字,以便基类方法将被派生类重写。但我不确定newsealed override

回答

74

虚拟关键字用于修改方法,属性,索引器或事件声明,并允许它在派生类中重写。例如,可以通过继承它的任何类来覆盖此方法: 使用新修饰符可以显式隐藏从基类继承的成员。要隐藏继承的成员,请使用相同名称在派生类中声明它,然后使用新修饰符对其进行修改。

这与多态性有关。当在引用上调用虚拟方法时,引用引用的对象的实际类型将用于决定使用哪种方法实现。在派生类中重写基类的方法时,即使调用代码没有“知道”该对象是派​​生类的实例,也会使用派生类中的版本。例如:

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

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

... 

Base d = new Derived(); 
d.SomeMethod(); 

最终将调用Derived.SomeMethod(如果覆盖Base.SomeMethod)。现在

,如果使用的关键字,而不是覆盖的,在派生类中的方法不重写基类中的方法,它只是隐藏它。在这种情况下,代码如下:

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

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

... 


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

首先调用Base.SomeOtherMethod,然后是Derived.SomeOtherMethod。它们实际上是两个完全分开的方法,它们碰巧具有相同的名称,而不是派生方法重写基本方法。

如果您没有指定新的或覆盖,则结果输出与您指定新的相同,但您也会收到编译器警告(因为您可能不知道隐藏了某个方法在基类方法中,或者实际上你可能想要覆盖它,并且只是忘记了包含关键字)。

重写属性声明可能包括密封修饰符。使用此修饰符可防止派生类进一步覆盖该属性。密封属性的访问者也被密封。

+0

感谢您的意见..但有一件事并没有进入我的脑海中,是什么使用Base b = new Derived()?这是创建基类或派生类的对象? – 2011-05-28 15:49:05

+2

派生类。我认为你必须更多地关注多态。这是您阅读的好帮手。 http://msdn.microsoft。com/en-us/library/ms173152(v = vs.80).aspx – CharithJ 2011-05-28 15:54:51

+3

@Xor:在这种情况下,您正在创建一个Derived对象的实例并将该引用存储在Base变量中。这是有效的,因为Derived对象也是一个Base对象。这就像说我们需要一个“人”,所以我们得到了“约翰尼”,他恰好是一个人。这里同样如此。 – 2011-05-28 15:58:28

15

默认情况下,方法不能在派生类中重写,除非声明为virtualabstractvirtual意思是在调用abstract意味着相同之前检查更新的实现,但它保证在所有派生类中被覆盖。而且,基类中不需要实现,因为它将在别处被重新定义。

上述例外是new修饰符。未声明的方法virtualabstract可以用派生类中的new修饰符重新定义。当在基类中调用方法时执行基方法,并且在派生类中调用该方法时,将执行新方法。所有new关键字允许您执行的操作是在类层次结构中使用

最后一个sealed修饰符打破了virtual方法的链并使它们不能再次覆盖。这不是经常使用,但选项在那里。更有意义与3类的链与前一个

A -> B -> C 

如果A具有virtualabstract方法中的每个导出,也就是在Boverridden,那么它也能防止C更改它再次通过声明它sealedB

sealed也用于classes,那就是你会经常遇到这个关键字的地方。

我希望这会有所帮助。

29

任何方法都可以被覆盖(= virtual)或不可以。

class Person 
{ 
    // this one is not overridable (not virtual) 
    public String GetPersonType() 
    { 
     return "person"; 
    } 

    // this one is overridable (virtual) 
    public virtual String GetName() 
    { 
     return "generic name"; 
    } 
} 

现在,您可以覆盖那些重写的方法:该决定是由一个谁定义的方法制成

class Friend : Person 
{ 
    public Friend() : this("generic name") { } 

    public Friend(String name) 
    { 
     this._name = name; 
    } 

    // override Person.GetName: 
    public override String GetName() 
    { 
     return _name; 
    } 
} 

但是你不能覆盖GetPersonType的方法,因为它不是虚拟的。

让我们创建这些类的两个实例:

Person person = new Person(); 
Friend friend = new Friend("Onotole"); 

当非虚方法GetPersonTypeFiend实例调用它实际上Person.GetPersonType被称为:

Console.WriteLine(friend.GetPersonType()); // "person" 

当虚方法GetName被称为通过Friend实例它是Friend.GetName被称为:

Console.WriteLine(friend.GetName()); // "Onotole" 

当虚拟方法GetNamePerson例如它的Person.GetName被称为所谓:

Console.WriteLine(person.GetName()); // "generic name" 

当非虚方法调用该方法的身体没有抬头 - 编译器已经知道需要的实际方法被称为。而对于虚拟方法,编译器无法确定应该调用哪一个,并且在运行时在类层次结构中从下往上查找它,从调用该方法的实例类型开始:对于friend.GetName,它看起来开始于Friend类,并立即找到它,因为person.GetName类开始于Person,并在那里找到它。

有时候,你让一个子类,重写虚方法,你不希望任何更多的覆盖下层次结构中的 - 你用sealed override为(说你是最后一个谁覆盖的方法):

class Mike : Friend 
{ 
    public sealed override String GetName() 
    { 
     return "Mike"; 
    } 
} 

但有时你的朋友迈克决定他的性别,因此他的名字改为爱丽丝:)你既可以改变原来的代码或代替继承迈克:

class Alice : Mike 
{ 
    public new String GetName() 
    { 
     return "Alice"; 
    } 
} 

在这里,您创建具有相同名称的完全不同的方法(轮到你了有两个)。哪种方法和什么时候被调用?这要看你怎么称呼它:

Alice alice = new Alice(); 
Console.WriteLine(alice.GetName());    // the new method is called, printing "Alice" 
Console.WriteLine(((Mike)alice).GetName());  // the method hidden by new is called, printing "Mike" 

当您从Alice调用它的角度来看,你叫Alice.GetName,当从Mike的 - 你叫Mike.GetName。这里没有运行时查找 - 因为这两种方法都是非虚拟的。

您始终可以创建new方法 - 您隐藏的方法是否为虚拟方法。

这也适用于属性和事件 - 它们被表示为下面的方法。

+0

没有比我在任何地方发现的简单而完整的答案。谢谢Loki – Reevs 2018-02-27 19:22:49

5
public class Base 
{ 
    public virtual void SomeMethod() 
    { 
    Console.WriteLine("B"); 
    } 
    } 

    //This one is Simple method 
public class Derived : Base 
{ 
    public void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 

    //This method has 'new' keyword 
    public new void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 

    //This method has 'override' keyword 
    public override void SomeMethod() 
    { 
    Console.WriteLine("D"); 
    } 
} 

现在第一件事首先

Base b=new Base(); 
Derived d=new Derived(); 
b.SomeMethod(); will always write B 
d.SomeMethod(); will always write D 

现在的关键词是所有关于多态性

       Base b = new Derived(); 
  1. 使用virtual在基类和Derived覆盖会给d(多态性)。
  2. 使用override没有virtualBase会给出错误。
  3. virtual类似地写一个方法(没有覆盖)将写入'B'警告(因为没有多态性完成)。
  4. 要隐藏这样的警告,在上面的点new之前,在那个简单的方法Derived
  5. new关键字是另一个故事,它只是隐藏警告,告诉同名的属性存在于基类中。
  6. virtualnew两者都是相同的,除了 new modifier

  7. newoverride之前相同的方法或特性可以不被使用。

  8. sealed之前任何类或方法锁定它将被用于派生类,它会给编译时错误。
+0

对不起,但-1因为多个编译错误:使用相同参数多次声明方法,字符串B&D周围没有引号... – DeveloperDan 2016-02-08 14:46:51