C#中的override和new关键字
问题1: 虚方法
首先,看下面的代码。
namespace CSharpTest
{
class A
{
public void fun()
{
Console.WriteLine("这是父类方法");
}
}
class B : A
{
public void fun()
{
Console.WriteLine("这是子类方法");
}
}
class Test
{
public static void Main()
{
A a = new A();
a.fun();
a = new B();
a.fun();
}
}
}
猜猜结果是什么? 如果对Java熟悉的朋友,可能会认为是结果:
这是父类方法
这是子类方法
但是其实运行结果是:
这是父类方法
这是父类方法
这是因为Java中的类方法默认是虚函数(虽然Java中没有这个叫法),子类函数会默认覆盖父类的同名函数(Java后来提供了@override注解)。然而C#中必须使用virtual关键字显示声明该函数是虚函数,然后在子类中使用override关键字重写父类方法,这才真正实现了对父类方法的重写,才能实现多态(C++中的多态就是使用虚函数实现的,而且和C#一样必须使用virtual关键字显示声明)。
所以,要输出结果为:
这是父类方法
这是子类方法
需要修改代码如下:
namespace CSharpTest
{
class A
{
public virtual void fun()
{
Console.WriteLine("这是父类方法");
}
}
class B : A
{
public override void fun()
{
Console.WriteLine("这是子类方法");
}
}
class Test
{
public static void Main()
{
A a = new A();
a.fun();
a = new B();
a.fun();
}
}
}
即给父类的方法添加virtual关键字,给子类的同名方法添加override关键字。
问题2:override和new关键字
首先看下面的代码:
namespace CSharpTest
{
class C
{
public virtual void fun()
{
Console.WriteLine("这是一个虚方法");
}
}
class C1 : C
{
public override void fun()
{
Console.WriteLine("使用override关键字修饰的方法");
}
}
class C2 : C
{
public new void fun()
{
Console.WriteLine("使用new关键字修饰的方法");
}
}
class Test
{
public static void Main()
{
C c1 = new C1();
c1.fun();
C c2 = new C2();
c2.fun();
}
}
}
猜猜上面的代码输出结果? 正确的结果是:
使用override关键字修饰的方法
这是一个虚方法
为什么使用关键字new修饰的方法,调用的是父类的方法呢? 是不是很奇怪?为什么使用override关键字的子类方法被调用了,而使用new关键字的子类方法没有被调用。 首先看看override关键字:override方法为从基类继承的成员提供新的实现。以override声明重写的方法被称为被重写的基类方法,被重写的基类方法必须具有与重写方法相同的签名。非虚方法或者静态方法不能被重写,被重写的基类必须是virtual、abstract或者override的。override声明不能改变虚方法的可访问性,override方法和virtual方法必须具有相同的访问级别修饰符。不能使用下列修饰符修饰重写方法:new、static、virtual和abstract。 new关键字:new修饰符用来明确地隐藏由基类继承而来的成员。要隐藏继承而来的成员,可以在派生类中共用相同的名称并用new修饰符修饰它。 下面来分析我们的程序: c1.fun();因为子类C1使用override关键字重写了父类的方法,基类C和子类C1都具有fun()方法,所以c1.fun()会动态调用C1的fun()方法而不是父类的。 c2.fun();子类C2使用new关键字隐藏了父类的方法,相当于子类中的fun()方法是直接继承自父类的。而子类中使用new关键字声明的fun()方法是另一个方法,只是恰巧与子类的fun()方法同名而已(是不是有些糊涂了)。所以 c2.fun()会调用父类的fun()方法,要想调用C2的fun()方法必须吧c2强制转换为C2.
下面看看微软官方的文档解释: C# 语言经过专门设计,以便不同库中的基类与派生类之间的版本控制可以不断向前发展,同时保持向后兼容。这具有多方面的意义。例如,这意味着在基类中引入与派生类中的某个成员具有相同名称的新成员在 C# 中是完全支持的,不会导致意外行为。它还意味着类必须显式声明某方法是要重写一个继承方法,还是一个隐藏具有类似名称的继承方法的新方法。 在 C# 中,派生类可以包含与基类方法同名的方法。 基类方法必须定义为 virtual。 如果派生类中的方法前面没有 new 或 override 关键字,则编译器将发出警告,该方法将有如存在 new 关键字一样执行操作。 如果派生类中的方法前面带有 new 关键字,则该方法被定义为独立于基类中的方法。 如果派生类中的方法前面带有 override 关键字,则派生类的对象将调用该方法,而不是调用基类方法。 可以从派生类中使用 base 关键字调用基类方法。 override、virtual 和 new 关键字还可以用于属性、索引器和事件中。 默认情况下,C# 方法为非虚方法。如果某个方法被声明为虚方法,则继承该方法的任何类都可以实现它自己的版本。若要使方法成为虚方法,必须在基类的方法声明中使用 virtual 修饰符。然后,派生类可以使用 override 关键字重写基虚方法,或使用 new 关键字隐藏基类中的虚方法。如果 override 关键字和 new 关键字均未指定,编译器将发出警告,并且派生类中的方法将隐藏基类中的方法。
- 2017 Multi-University Training Contest - Team 9 1005&&HDU 6165 FFF at Valentine【强联通缩点+拓扑排序】
- 2017 Multi-University Training Contest - Team 9 1004&&HDU 6164 Dying Light【数学+模拟】
- Python3选择排序
- 【DeepMind 公开课-深度强化学习教程代码实战01】迭代法评估4*4方格世界下的随机策略
- Codeforces Round #434 (Div. 2, based on Technocup 2018 Elimination Round 1)&&Codeforces 861C Did yo
- Codeforces Round #434 (Div. 2, based on Technocup 2018 Elimination Round 1)&&Codeforces 861B Which
- 信用卡安全问题:被用户忽视的识别码
- Python3快速排序
- Python3插入排序
- Python3冒泡排序
- Python Selenium设计模式-POM
- 【Python学习笔记之一】Python关键字及其总结
- 前后端分离了,然后呢?
- 【Python学习笔记之二】浅谈Python的yield用法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- php和html的区别点详细总结
- 详解在YII2框架中使用UEditor编辑器发布文章
- 在Laravel的Model层做数据缓存的实现
- Thinkphp5.0 框架使用模型Model添加、更新、删除数据操作详解
- php连接mysql数据库最简单的实现方法
- 解决laravel id非自增 模型取回为0 的问题
- PHP7.0连接DB操作实例分析【基于mysqli】
- Laravel获取当前请求的控制器和方法以及中间件的例子
- laravel中数据显示方法(默认值和下拉option默认选中)
- laravel请求参数校验方法
- ThinkPHP 5.x远程命令执行漏洞复现
- 关于php开启错误提示的总结
- php创建类并调用的实例方法
- php异常处理捕获错误整理
- 可视化IDE的探索之路