使用 Override 和 New 关键字进行版本控制

C# 语言经过专门设计,以便不同库中的基类与派生类之间的版本控制可以不断向前发展,同时保持向后兼容。 这具有多方面的意义。例如,这意味着在基类中引入与派生类中的某个成员具有相同名称的新成员在 C# 中是完全支持的,不会导致意外行为。 它还意味着类必须显式声明某方法是要重写一个继承方法,还是一个隐藏具有类似名称的继承方法的新方法。

在 C# 中,派生类可以包含与基类方法同名的方法。
基类方法必须定义为 virtual。
如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。
如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。
如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。
可以从派生类中使用 base 关键字调用基类方法。
override 、virtual 和 new 关键字还可以用于属性、索引器和事件中。
默认情况下,C# 方法为非虚方法。 如果某个方法被声明为虚方法,则继承该方法的任何类都可以实现它自己的版本。 若要使方法成为虚方法,必须在基类的方法声明中使用 virtual 修饰符。 然后,派生类可以使用 override 关键字重写基虚方法,或使用 new 关键字隐藏基类中的虚方法。 如果 override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。
为了在实践中演示上述情况,我们暂时假定公司 A 创建了一个名为 GraphicsClass 的类,您的程序将使用此类。 GraphicsClass 如下所示:

C#

class GraphicsClass
{
  public virtual void DrawLine() { }
  public virtual void DrawPoint() { }
}

您的公司使用此类,并且您在添加新方法时将其用来派生自己的类:
C#

class YourDerivedGraphicsClass : GraphicsClass
{
  public void DrawRectangle() { }
}

您的应用程序运行正常,直到公司 A 发布了 GraphicsClass 的新版本,类似于下面的代码:
C#

class GraphicsClass
{
  public virtual void DrawLine() { }
  public virtual void DrawPoint() { }
  public virtual void DrawRectangle() { }
}

现在,GraphicsClass 的新版本中包含一个名为 DrawRectangle 的方法。 开始时,没有出现任何问题。 新版本仍然与旧版本保持二进制兼容。 已经部署的任何软件都将继续正常工作,即使新类已安装到这些软件所在的计算机系统上。 在您的派生类中,对方法 DrawRectangle 的任何现有调用将继续引用您的版本。
但是,一旦您使用 GraphicsClass 的新版本重新编译应用程序,就会收到来自编译器的警告 CS0108。 此警告提示您必须考虑希望 DrawRectangle 方法在应用程序中的工作方式。
如果您希望自己的方法重写新的基类方法,请使用 override 关键字:
C#

class YourDerivedGraphicsClass : GraphicsClass
{
  public override void DrawRectangle() { }
}

override 关键字可确保派生自 YourDerivedGraphicsClass 的任何对象都将使用 DrawRectangle 的派生类版本。 派生自 YourDerivedGraphicsClass 的对象仍可以使用基关键字访问 DrawRectangle 的基类版本:
C#

base.DrawRectangle();

如果您不希望自己的方法重写新的基类方法,则需要注意以下事项。 为了避免这两个方法之间发生混淆,可以重命名您的方法。 这可能很耗费时间且容易出错,而且在某些情况下并不可行。 但是,如果您的项目相对较小,则可以使用 Visual Studio 的重构选项来重命名方法。 
或者,也可以通过在派生类定义中使用关键字 new 来防止出现该警告:
C#

class YourDerivedGraphicsClass : GraphicsClass
{
  public new void DrawRectangle() { }
}

使用 new 关键字可告诉编译器您的定义将隐藏基类中包含的定义。 这是默认行为。

重写和方法选择
当在类中指定方法时,如果有多个方法与调用兼容(例如,存在两种同名的方法,并且其参数与传递的参数兼容),则 C# 编译器将选择最佳方法进行调用。 下面的方法将是兼容的:
C#

public class Derived : Base
{
  public override void DoWork(int param) { }
  public void DoWork(double param) { }
}

在 Derived 的一个实例中调用 DoWork 时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 DoWork 版本兼容。 重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。 仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配。 例如:
C#

int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).

由于变量 val 可以隐式转换为 double 类型,因此 C# 编译器将调用 DoWork(double),而不是 DoWork(int)。 有两种方法可以避免此情况。 首先,避免将新方法声明为与虚方法同名。 其次,可以通过将 Derived 的实例强制转换为 Base 来使 C# 编译器搜索基类方法列表,从而使其调用虚方法。 由于是虚方法,因此将调用 Derived 上的 DoWork(int) 的实现。 例如:
C#

((Base)d).DoWork(val); // Calls DoWork(int) on Derived.

使用 Override 和 New 关键字进行版本控制的更多相关文章

  1. 第2节 Scala中面向对象编程:7、继承的概念以及override和super关键字;8、isInstanceOf 和 asInstanceOf关键字

    6.3.   Scala面向对象编程之继承 6.3.1.     Scala中继承(extends)的概念 Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字: 继承 ...

  2. C#多态;父类引用指向子类对象;new和override的区别;new、abstract、virtual、override,sealed关键字区别和使用代码示例;c#类的初始化顺序

    关于父类引用指向子类对象 例如: 有以下2个类 public class Father { public int age = 70; public static string name = " ...

  3. 浅析C#中new、override、virtual关键字的区别

    Virtual : virtual 关键字用于修饰方法.属性.索引器或事件声明,并使它们可以在派生类中被重写. 默认情况下,方法是非虚拟的.不能重写非虚方法. virtual 修饰符不能与 stati ...

  4. C++11 override 和 final 关键字

    C++11之前,一直没有继承控制关键字.禁用一个类的进一步衍生是可能的但也很棘手.为避免用户在派生类中重载一个虚函数,你不得不向后考虑. C++ 11添加了两个继承控制关键字:override和fin ...

  5. 接口与virtual,override,new关键字

    一,类继承接口 1,首先我们定义一个简单的ITeacher接口,并定义一个Professor类继承它. public interface ITeacher { void Print(); } publ ...

  6. override和new关键字 隐藏父类的方法

    正常情况下,父类virtual的方法,子类override class Animal { public virtual void EatFood() { Console.WriteLine(" ...

  7. ylbtech-LanguageSamples-Versioning(版本控制)

    ylbtech-Microsoft-CSharpSamples:ylbtech-LanguageSamples-Versioning(版本控制) 1.A,示例(Sample) 返回顶部 “版本控制”示 ...

  8. c# override用法

    要扩展或修改继承的方法.属性.索引器或事件的抽象实现或虚实现,必须使用 override 修饰符. 在此例中,类 Square 必须提供 Area 的重写实现,因为 Area 是从抽象的 Shapes ...

  9. C# 4.0 的 Visual Studio 2010 示例

    C# 4.0 的 Visual Studio 2010 示例 我们将 C# 示例分为两种不同的类别: 语言示例 LINQ 示例 语言示例 语言示例帮助您熟悉各种 C# 语言功能.这些示例包含在 Lan ...

随机推荐

  1. 关于string的练习题目

    /*Are they equal*/#include<iostream>#include<string>using namespace std;int n;string dea ...

  2. My97DatePickerBeta 时间选择控件用法

    用法说明:只需要调用一个js<script language="javascript" type="text/javascript" src=" ...

  3. Spring MVC数组绑定

    需求:商品批量删除,用户在页面选择多个商品,批量删除. 关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id // 批量删除 商品信息 @ ...

  4. sgu548 Dragons and Princesses   贪心+优先队列

    题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=548 题目意思: 有一个骑士,要经过n个房间,开始在第一个房间,每个房间里面有龙或者 ...

  5. mysql 5.5及以前版本的编码问题“Incorrect string value: '\xE6\x9B\xB9\xE5\x86\xAC...' for column 'realname' at row 1”

    遇到这个问题,所有的编码都设为utf8了,还是没有用,各种乱码,后来发现这是mysql自己的问题,它在5.5及之前的版本只支持3字节的utf8编码,出现4字节的utf编码时出现错误,参考: http: ...

  6. 越狱Season 1- Episode 16

    Season 1, Episode 16 -Burrows:Don't be. It's not your fault. 不要,不是你的错 -Fernando: Know what I like? 知 ...

  7. 【BZOJ1012】【JSOI2008】最大数

    难得的水题,应该是这题是T1吧,终于没看别人代码,然而思路还是看题解点了一下 原题: 现在请求你维护一个数列,要求提供以下两种操作:1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大 ...

  8. Linux驱动设计—— 中断与时钟

    中断和时钟技术可以提升驱动程序的效率 中断 中断在Linux中的实现 通常情况下,一个驱动程序只需要申请中断,并添加中断处理函数就可以了,中断的到达和中断函数的调用都是内核实现框架完成的.所以程序员只 ...

  9. Android sdk 镜像服务器资源

    大连东软信息学院镜像服务器地址:- http://mirrors.neusoft.edu.cn 端口:80北京化工大学镜像服务器地址:- IPv4: http://ubuntu.buct.edu.cn ...

  10. 常见半监督方法 (SSL) 代码总结

    经典以及最新的半监督方法 (SSL) 代码总结 最近因为做实验需要,收集了一些半监督方法的代码,列出了一个清单: 1. NIPS 2015 Semi-Supervised Learning with ...