溯因推理是指根据事物发展过程所造成的结果。推断形成结果的一系列原因的整个逻辑思维过程,通俗的说就是从已知结果推断其原因的一种思维方式,其目的是得到一个最佳的解释,是一种典型的由果及因的方式。在逻辑结构上,它包括以下要素:
n 观察现象陈述
n 导致观察现象的可能原因即猜测性假说
在C#的面向对象中,override是使用比较多的关键字。但你是否知道为什么需要这两个关键字,并且当子类类型转为基类类型是,这两个关键字定义的成员将有什么不同吗?
在推理override和new的机制之前,我们再次回顾下这两个关键字的含义和用途。
n override:要扩展或修改继承的方法、属性、索引器或事件的抽象实现或虚实现,必须使用 override 修饰符(来自MSDN)。
n new:在用作修饰符时,new 关键字可以显式隐藏从基类继承的成员。隐藏继承的成员时,该成员的派生版本将替换基类版本(来自MSDN)。
在中文的术语中我们将override称为重写而new称为覆盖,并且MSDN在描述“何时使用 Override 和 New 关键字”时语焉不详,仅给出了以下的使用建议和原则:
在 C# 中,派生类可以包含与基类方法同名的方法,其定义的原则是:默认情况下,C# 方法为非虚方法。如果某个方法被声明为虚方法,则继承该方法的任何类都可以实现它自己的版本。若要使方法成为虚方法,必须在基类的方法声明中使用 virtual 修饰符。然后,派生类可以使用 override 关键字重写基虚方法,或使用 new 关键字隐藏基类中的虚方法。如果 override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。
为了解释这段另人头晕的文字,微软在MSDN中还给出了一个更令人膛目结舌的案例。不过为了让读者能快速清晰的了解这段文字想描述的含义,我将其总结为三条口诀:
n 任何非抽象(abstract)的实例方法在C#中默认是非虚拟(virtual)的
n 子类要定义和父类中非抽象(abstract)或非虚拟(virtual)的同名方法时,必须使用new(覆盖)关键字
n 子类要定义和父类中抽象或虚拟的同名方法时,必须使用override(重写)关键字
以下我们将实现一个继承的模型,这个模型中我将使用到override和new关键字,类的对象图如下
图Order类对象模型
以下我们将通过代码实现这个模型,请读者仔细观察和跟随实现。
我们首先定义一个Order类,该类只有两个方法,其中PrintInfo方法是虚拟的,其目的是输出一个简单的信息;PrintAuthor方法是非虚拟的,其目的是输出当前Windows操作系统登录人员的名字。
public class Order
{
public virtual void PrintInfo()
{
System.Console.WriteLine("Order...");
}
public void PrintAuhor()
{
System.Console.WriteLine(System.Environment.UserName);
}
}
然后我们从Order类继承一个派生类ShipOrder,该类使用override关键字重写了PrintInfo方法
public class ShipOrder : Order
{
public override void PrintInfo()
{
System.Console.WriteLine("ShipOrder");
}
}
我们再次从Order类继承一个派生类RoadOrder,该类使用override关键字重写了PrintInfo方法,并且使用new关键字覆盖了Order中非虚拟的PrintAuhor方法
public class RoadOrder : Order
{
public override void PrintInfo()
{
System.Console.WriteLine("RoadOrder");
}
public new void PrintAuhor()
{
System.Console.WriteLine(System.Environment.UserDomainName);
}
}
为展示这些类运行的结果,我们编写以下的代码
class Program
{
static void Main(string[] args)
{
new Order().PrintInfo();
new Order().PrintAuhor();
new ShipOrder().PrintInfo();
new ShipOrder().PrintAuhor();
new RoadOrder().PrintInfo();
new RoadOrder().PrintAuhor();
}
}
在查看显示结果前,笔者建议你自己先想像下运行的结果应该怎么样。下图是运行的结果
图
如果结果和你想像的一样,那么恭喜你,你对这两个关键字的基本用法已经非常明确了。但是现在我们想像下,如果将子类ShipOrder和RoadOrder强制的转为Order类型,并且再次调用PrintInfo和PrintAuthor方法会有什么情况出现呢?换句话说:将子类转换为父类的类型调用时,override和new方法是实现父类的行文还是自己自身的行为呢?
以下的运行代码演示如何进行这个测试
class Program
{
static void Main(string[] args)
{
new Order().PrintInfo();
new Order().PrintAuhor();
System.Console.WriteLine("----------------");
((Order)new ShipOrder()).PrintInfo();
((Order)new ShipOrder()).PrintAuhor();
System.Console.WriteLine("----------------");
((Order)new RoadOrder()).PrintInfo();
((Order)new RoadOrder()).PrintAuhor();
}
}
运行的结果如图,不知道和你想像的结果是否一致?
图
我们总结下运行的结果
当子类转换为父类,且以父类的形式进行方法调用时:override总是指向自己的实现,而new总是指向父类的实现。
那么为什么会出现这样的情况呢?请查看下目前我们所了解的事实:
当父类的方法定义为抽象(abstract)或虚拟(virtual)时,子类使用override关键字
当父类没有将方法定义为抽象(abstract)或虚拟(virtual)时,子类使用new关键字
现在我们将这个事实用表格的方式进行统计,统计的结果为: 父类方法的修饰
Abstract
virtual
默认
子类调用父类方法
失败
成功
成功
子类调用自己的方法
成功
成功
成功
子类同名方法定义关键字
override
override
new
从上面的表格我们可以清晰的得到如下的判断
n 因为override时候父类的成员有可能是abstract,所以调用其父类方法有50%的失败可能。
n 使用new关键字的时候,父类的成员必须是已经实现的,所以调用其父类决不可能会失败。
因此,当子类类型转换为基类类型时,为了确保最大的安全系数,使用override修饰的成员,都将指向子类自己的成员实现,而当成员是new定义的话,可以放心的调用父类类型中定义的成员了。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/shyleoking/archive/2010/05/20/5610612.aspx