virtual,override,new的区别

virtual
  用于父类中,声明某方法可以重写,该方法不能是static;
override(覆盖继承)
  用于子类中,重写父类中用virtual/override/abstract关键字声明的方法;
new(隐藏继承)
  用于子类中,可修饰父类中有或无virtual声明的方法;
  如果在父类中有与父类同名同参的方法时,C#会隐式在子类前添加一个new关键字。

【区别】override与new
  当将一个子类实例赋值给父类实例,在子类实例与父类实例中分别调用该方法时:使用override修饰的都将调用子类方法,而使用new修饰的会分别调用子类方法和父类方法。
  New和Override这两个关键字是互斥的。不可以同时使用。


【区别】virtual与abstract
  virtual:用于父类中声明该方法可被重写。
  abstract:只能用在抽象类中声明抽象方法,本身不能实现任何功能(因此必须在子类中override),不能实例化。
  两者除了在子类中同样可以用override重写这个相同点之外,没有其他关系。

 

-------------------------------------------------------------------------------------------------------------------

C#支持单继承,说到继承就不得不说new,virtual和override这三个关键词,灵活正确的使用这三个关键词,可以使程序结构更加清晰,代码重用性更高。
   

      以下是msdn中对new,virtual和override的定义:
      使用 new 修饰符显式隐藏从基类继承的成员。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。
      virtual 关键字用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员。虚拟成员的实现可由派生类中的重写成员更改。调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。默认情况下,方法是非虚拟的。不能重写非虚方法。
      virtual 修饰符不能将与以下修饰符一起使用:
      static    abstract    override
      使用 override 修饰符来修改方法、属性、索引器或事件。重写方法提供从基类继承的成员的新实现。由重写声明重写的方法称为重写基方法。重写基方法必须与重写方法具有相同的签名。不能重写非虚方法或静态方法。重写基方法必须是虚拟的、抽象的或重写的。
      重写声明不能更改虚方法的可访问性。重写方法和虚方法必须具有相同的访问级修饰符。
    
      override不能使用下列修饰符修改重写方法:
      new   static    virtual   abstract
      重写属性声明必须指定与继承属性完全相同的访问修饰符、类型和名称,并且重写属性必须是虚拟的、抽象的或重写的。
      可以稍微归纳一下:
      1. 对于基类中说明为虚的方法则必须在派生类中new或者override(注:对于基类的虚方法,虽然你在派生类中即不new也不override,但系统还是会提示你添关键字。否则系统将视其为隐藏。我们的意思是一样的,但总觉得明明确确写上关键字还是好些)。
      2. 如果用基类指针指向派生类对象的方式,动态匹配的源动力是virtual,而new和override都会阻止这种向下寻求匹配的行为,所以要使虚函数的性质得已保持下去,就要隐藏基类的虚方法,即在派生类中隐藏基类虚方法时,同时加以virtual关键字,使在多层次继承中能够调用到对象自身的版本。注:如 public new virtual  方法名()
      3.在多层次继承中,三个关键字使用次序有限定。
    new没有使用前提,即不管是普通方法、虚方法还是重写了的方法。
    virtual的使用,在它的基类不能有函数签名相同的方法,否则系统将提示添加new,即隐藏基类中的方法。virtual一般只出现一次,除非要在子类中隐藏父类的虚方法。
    
override的使用是为了重写基类虚方法。
     

  上面的描述都很抽象,对于初学者可能不好理解,下面我将用示例来说明这三个用法和区别:此程序在vs2005下调试通过。其中有三个类,分别

为基类BaseClass,继承类InheritClass和继承类的继承类GrandsonClass代码分别如下:

  


//BaseClass.cs
namespace NewVirtualOverride
{
class BaseClass
{
public BaseClass()
{
}
public void Print()
{
Console.WriteLine(
"BaseClassPrint");
}
}
}
//InheritClass.cs
namespace NewVirtualOverride
{
class InheritClass : BaseClass
{
public InheritClass():base()
{
}
public void Print()
{
Console.WriteLine(
"InheritClassPrint");
}
}
}
//GrandsonClass.cs
namespace NewVirtualOverride
{
class GrandsonClass : InheritClass
{
public GrandsonClass():base()
{
}
public void Print()
{
Console.WriteLine(
"GrandsonClassPrint");
}
}
}
//最后是主程序Program:
namespace NewVirtualOverride
{
class Program
{
static void Main(string[] args)
{
BaseClass baseclass
= new BaseClass();
baseclass.Print();
InheritClass inheritClass
= new InheritClass();
inheritClass.Print();
Console.ReadLine();
}
}
}
运行这个程序会得到如下的结果:
BaseClassPrint
InheritClassPrint
其实细心的朋友在编译这个项目时,会发现出现了如下的警告提示:

 


class InheritClass:BaseClass
{
public InheritClass():base()
{}

//New Virtual Override InheritClass.Print()隐藏了继承的成员
//New Virtual Override BaseClass.Print()。如果是刻意隐藏请使用new
public void Print()
{
Console.WriteLine(
"Hello");
}
}
   
     大致意思是说,基类和继承类中有相同名字的方法,请在继承类中使用new来重新定义方法。这里的微妙之处在于,无论我们是隐式地指定new方法,还是显式的指定,new方法都与基类中的方法无关,在名称、原型、返回类型和访问修饰符方面都无关。
     我们将程序中的Print()方法都变成new public void Print()后,上面的异常就不会发生了。再次运行程序,结果不变。new就是继承类使用与基类方法相同的名字对基类方法的重写。
     下面我们看看virtual 和 override的搭配使用方法。
     把BaseClass.cs改变如下:public virtual void Print();
     把InheritClass.cs改变如下:public override void Print();
     运行程序,结果如下:
     BaseClassPrint
     InheritClassPrint

 

     虽然结果与用new修饰符差不多,,但是其中的含意可不同,new是继承类对基类方法的重写而在继承类中产生新的方法,这时基类方法和继承方法之间没有任何的关系了,可是override就不同了,它也是对基类中方法的重写,但此时只是继承类重写了一次基类的方法。可以参考下面的例子来加深理解。

将Program.cs改变如下:

 


BaseClass baseclass = new BaseClass();
baseclass.Print();
InheritClass inheritClass
= new InheritClass();
inheritClass.Print();
BaseClass bc
= new InheritClass();
bc.Print();
   分别运行用new修饰和用virtual/override修饰的程序,其结果如下:
    用new修饰的结果
   BaseClassPrint
   InheritClassPrint
   BaseClassPrint
    用virtual/override修饰的结果:
   BaseClassPrint
   InheritClassPrint
   InheritClassPrint
   从上面的结果可以看出,在用new修饰的情况下,虽然bc是用InheritClass创建的实例,但是bc.Print()打印的还是BaseClassPrint,因为此时BaseClass和InheritClass中的Print已经是互不相同没有关系的两个方法了,而在virtual/override修饰的情况下,bc调用的Print方法已经被其子类override了,所以就打印了InheritClassPrint。

 

  最后我们再说说关键词之间的搭配关系,上面已经给出了virtual和override不兼容的几个关键词,这里就不重复了。我要说的是new和virtual在声明函数时,其实可以一块使用。因为这个函数是新的,故与其它任何new函数一样,隐藏了具有相同原型的继承来的函数。因为这个函数也是虚拟的,所以可以在派生类中进一步复位义,这样就为这个虚拟函数建立了一个新的基级别。最后用GrandsonClass类来看看。


将GrandsonClass.cs修改如下:
namespace NewVirtualOverride
{
class GrandsonClass : InheritClass
{
public GrandsonClass():base()
{
}
public override void Print()
{
Console.WriteLine(
"GrandsonClassPrint");
}
}
}
InheritClass.cs修改如下:
namespace NewVirtualOverride
{
class InheritClass : BaseClass
{
public InheritClass():base()
{
}
new public virtual void Print()
{
Console.WriteLine(
"InheritClassPrint");
}
}
}
BaseClass.cs修改如下:
namespace NewVirtualOverride
{
class BaseClass
{
public BaseClass()
{
}
public virtual void Print()
{
Console.WriteLine(
"BaseClassPrint");
}
}
}
Program.cs修改如下:
namespace NewVirtualOverride
{
class Program
{
static void Main(string[] args)
{
BaseClass baseclass
= new BaseClass();
baseclass.Print();

InheritClass inheritClass
= new InheritClass();
inheritClass.Print();

BaseClass grandsonClass
= new GrandsonClass();
grandsonClass.Print();\
Console.ReadLine();
}
}
}

 

    运行结果为:
    BaseClassPrint
    InheritClassPrint
    BaseClassPrint
    可见在InheritClass中使用了new以后,就意味着它与基类的同名方法为两个不同方法了,而它又是虚拟的,所以它的子类还可以继续继承BaseClass的Print()方法。
    将函数声明为virtual 与将它声明为new virtual是一样的,因为new仍然是默认的。所以下面的两句是相同的:
    public new virtual void Print(); public virtual void Print();
    那么new virtual的意义又在什么地方呢?在大型的层次结构中,这可能很有用,比如如下的System.Windows.Form类的继承关系Object->MarshalByRefObject->Component->Control->ScrollableControl->
ContainerControl。
    很容易想象出将一个派生的窗体集合作为窗体对待,而不是作为Object的情形。
再将Program.cs修改如下:

namespace NewVirtualOverride
{
class Program
{
static void Main(string[] args)
{
BaseClass baseclass
= new BaseClass();
baseclass.Print();
InheritClass inheritClass
= new InheritClass();
inheritClass.Print();
BaseClass grandsonClass1
= new InheritClass();
grandsonClass1.Print();
InheritClass grandsonClass2
= new GrandsonClass();
grandsonClass2.Print();
Console.ReadLine();
}
}
}

 

运行结果为:
BaseClassPrint
InheritClassPrint
BaseClassPrint
GrandsonClassPrint
posted @ 2009-07-05 12:16  .NET钉子户  阅读(780)  评论(1)    收藏  举报