C#方法的重载、重写和隐藏
方法的重载指的是同一个类型中,允许有同名的方法,但是,这些方法的输入参数必须不同,例如,参数类型或参数个数。
方法的重写会使得派生类型的方法表中,重写的成员抹掉基类的虚方法,自己取而代之。
测试代码如下:
另外,可以使用 sealed 关键字配合 override 关键字,禁止子类对方法进一步重写。
而方法的隐藏通过 new 关键字显式实现(实际上,标不标 new 效果是一样的,标 new 只会消除编译器的警告),分两种情况:
方法的隐藏没什么特别的,不过是父子类方法撞名了而已,和普通的不撞名情况没有区别, 在运行时调用哪个,取决于对象编译时类型是什么。
从本质上来说,当父子类存在同名的方法时,有两种隐藏策略:按名字 (hide by name) 和按签名 (hide by signature),C# 使用后者,也就是说,只有方法名和输入参数完全相同时,才构成隐藏。
在方法重写时,子类方法的可访问性必须和父类相同,不可以收紧也不可以放宽,这是因为,CLR 承诺子类总可以转型为父类,如果子类的方法访问和父类条件不同,那么这个转型就会岀问题。不过,方法的隐藏没有这个限制。
趁热打铁一把,来看下面的例子:
如果牵扯到可变数量的参数,那么带有可变数量参数的方法,和普通方法不同。C# 会优先调用普通方法。例如:注意:由或不由 ref/out 修饰,在编译器眼中是一样的。
public void NormalMethod(int a,params int[] b) { Console.WriteLine("2"); } public void NormalMethod(int a, intb) { Console.WriteLine("1"); }看看这两个方法,其实第一个方法已经包括了第二个,但是编译器并不会报错。如果我们调用时,传入 2 个整数,那么会调用第二个方法打印 1 :
var a = new ExampleRef(); a.NormalMethod(1,2);C# 会先查找没有使用 params 的方法,如果找不到匹配的方法,才会去找带有 paiams 的方法。
方法的重写和隐藏涉及一对父子类型。如果父类型有一个虚方法,那么,子类型可以使用 override 显式地重写该方法,或者使用 new 隐藏该方法。提示:只有方法的最后一个参数才可以使用 params 关键字。
方法的重写会使得派生类型的方法表中,重写的成员抹掉基类的虚方法,自己取而代之。
测试代码如下:
class Program { static void Main(string[] args) { A c1 = new C(); c1.Foo(); C c2 = new C(); c2.Foo(); Console.ReadKey(); } } class A { public virtual void Foo() { Console.WriteLine("Call on A.Foo()"); } } class B :A { public override void Foo() { Console.WriteLine("Call on B.Foo()"); } } class C :B { public new void Foo() { Console.WriteLine("Call on C.Foo()"); } }非常简单直接,B 重写了 A 的方法,而 C 隐藏了 B 的方法。
另外,可以使用 sealed 关键字配合 override 关键字,禁止子类对方法进一步重写。
而方法的隐藏通过 new 关键字显式实现(实际上,标不标 new 效果是一样的,标 new 只会消除编译器的警告),分两种情况:
- 父类是虚方法,则子类的方法表包括子类和父类的方法。
- 父类是普通方法,则子类的方法表不包括父类的方法。
方法的隐藏没什么特别的,不过是父子类方法撞名了而已,和普通的不撞名情况没有区别, 在运行时调用哪个,取决于对象编译时类型是什么。
从本质上来说,当父子类存在同名的方法时,有两种隐藏策略:按名字 (hide by name) 和按签名 (hide by signature),C# 使用后者,也就是说,只有方法名和输入参数完全相同时,才构成隐藏。
在方法重写时,子类方法的可访问性必须和父类相同,不可以收紧也不可以放宽,这是因为,CLR 承诺子类总可以转型为父类,如果子类的方法访问和父类条件不同,那么这个转型就会岀问题。不过,方法的隐藏没有这个限制。
趁热打铁一把,来看下面的例子:
class Program { static void Main(string[] args) { //父类的对象访问父类的方法,去A的方法表 A a = new A(); a.NormalMethod(); a.NewMethod(); //子类的对象访问子类的方法,去B的方法表 B b = new B(); b.NormalMethod(); b.NewMethod(); //出现这种情况时,a2 的运行时类型为B,它会去B的方法表 //但它的编译时对象为A A a2 = new B(); //B的方法表已经重写了该方法,访问不到父类的方法,只能访问子类的方法 a2.NormalMethod(); //B的方法表中有两个NewMethod,根据编译时的类型,调用父类方法 a2.NewMethod(); Console.ReadKey(); } } class A { public virtual void NormalMethod() { Console.WriteLine("A.NormalMethod"); } public virtual void NewMethod() { Console.WriteLine("A.NewMethod"); } } class B :A { public override void NormalMethod() { Console.WriteLine("B.NormalMethod"); } public new void NewMethod() { Console.WriteLine("B.NewMethod"); } }输出结果如下所示。
A.NormalMethod
A. NewMethod
B. NormalMethod
B.NewMethod
B.NormalMethod
A.NewMethod
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算