覆盖与重载(override/overload) [C#]
1. 方法签名与方法的显式隐藏
以下程序中,子类B与父类A存在签名相同的函数,将产生方法隐藏。由于没有显式使用new修饰符,编译时出现警告。
签名相同简单的讲是指忽略访问控制符、函数返回值、参数名后其它内容相同。
如:internal int Print(int x)
public void Print(int y)
protected float Print(int z)
忽略访问控制符、返回值与参数名后都变成了Print(int),所以它们都是签名相同的函数。
public int Print(int x, int y) 和 public int Print(int x) 属于不同签名的函数
public int Print(int X) 和 public int Print(float x) 属于不同签名的函数
当new关键字用作修饰符时,可以在派生类中隐藏基类的方法,也就说在派生类的方法是new关键字新定义出来的方法,而不是基类的方法。在不使用New关键字来隐藏基类方法也是可以的,编译器会出现一个警告,提示如果有意隐藏基类的方法,请使用New关键字修饰。
这种方法不应该过多地使用,因为它破坏了类型之间良好的继承关系,容易造成理解和维护上的困难。
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
class A
{
internal void Print( int x)
{
Console.WriteLine( " A: {0} " , x);
}
}
class B : A
{
// 当A中的Print()成员用public、protected、internal修饰,B从A继承了Print()方法时,会出现如下警告:
// "LearnCSharp.B.Print()"隐藏了继承的成员"LearnCSharp.A.Print()",如果是有意隐藏,请使用关键字new
public void Print( int y)
{
Console.WriteLine( " B: {0} " , y);
}
}
class P
{
public static void Main( string [] args)
{
A a = new A();
a.Print( 3 );
Console.ReadKey();
B b = new B();
b.Print( 4 );
Console.ReadKey();
}
}
}
输出:
A: 3
B: 4
用关键字new,"LearnCSharp.B.Print()"显式隐藏了继承的成员"LearnCSharp.A.Print()":
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
class A
{
internal void Print( int x)
{
Console.WriteLine( " A: {0} " , x);
}
}
class B : A
{
public new void Print( int y)
{
Console.WriteLine( " B: {0} " , y);
}
}
class P
{
public static void Main( string [] args)
{
A a = new A();
a.Print( 3 );
Console.ReadKey();
B b = new B();
b.Print( 4 );
Console.ReadKey();
}
}
}
输出:
A: 3
B: 4
把基类中的Print()方法变成virtual方法后:
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
class A
{
internal virtual void Print( int x)
{
Console.WriteLine( " A: {0} " , x);
}
}
class B : A
{
// 警告:"LearnCSharp.B.Print()"将隐藏继承的成员"LearnCSharp.A.Print()"。若要使当前成员重写该实现,
// 请添加关键字override,否则添加关键字new。
public void Print( int y)
{
Console.WriteLine( " B: {0} " , y);
}
}
class P
{
public static void Main( string [] args)
{
A a = new A();
a.Print( 3 );
Console.ReadKey();
B b = new B();
b.Print( 4 );
Console.ReadKey();
}
}
}
输出:
A: 3
B: 4
添加override关键字后:
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
class A
{
public virtual void Print( int x)
{
Console.WriteLine( " A: {0} " , x);
}
}
class B : A
{
public override void Print( int y)
{
Console.WriteLine( " B: {0} " , y);
}
}
class P
{
public static void Main( string [] args)
{
A a = new A();
a.Print( 3 );
Console.ReadKey();
B b = new B();
b.Print( 4 );
Console.ReadKey();
}
}
}
A: 3
B: 4
添加关键字abstract将类A变成抽象类 后,无法创建 抽象类或接口“LearnCSharp.A”的实例 。修改后为:
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
abstract class A
{
public virtual void Print( int x)
{
Console.WriteLine( " A: {0} " , x);
}
}
class B : A
{
public override void Print( int y)
{
Console.WriteLine( " B: {0} " , y);
}
}
class P
{
public static void Main( string [] args)
{
B b = new B();
b.Print( 4 );
Console.ReadKey();
}
}
}
B: 4
以下程序展示了new和override的本质区别 :
using System.Collections.Generic;
using System.Text;
namespace LearnCSharp
{
abstract class A
{
public virtual void Print()
{
Console.WriteLine( " 这是虚方法 " );
}
}
class B1 : A
{
public override void Print()
{
Console.WriteLine( " 这是新的方法 " );
}
}
class B2 : A
{
public new void Print()
{
Console.WriteLine( " 这是另一个新的方法 " );
}
}
class P
{
public static void Main( string [] args)
{
A a1 = new B1();
A a2 = new B2();
a1.Print();
a2.Print();
Console.ReadKey();
}
}
}
输出:
这是新的方法
这是虚方法
总结:New关键字主要用来区别派生类和基类同名方法的选择问题,通过隐藏基类方法,达到使编译器调用正确的方法的目的。Override主要用来对基类的方法和虚方法进行重写。
2. 方法重载与重写(overload & override)
override表示“重写”,用于继承一个基类的时候,基类当中虚拟成员的实现。一般语境里,如果
说这个method(方法)是被override来的,就是说在定义这个方法的类的父类中有一个与这个方法同名且参数类型列表相同的方法,在子类中,这个
方法被override了。在对这个子类的实例调用该方法时,编译器确切的知道调用的是这个子类的方法。override指它随时随地都只有一种含义。
overload表示“重载”,用于同一类中同名方法但参数个数或类型不同的实现,也就是让方法有不同签名的版本。一般语境里overload是
对method(方法)而言的,可以指一个类中多个名字相同而参数类型列表不相同的方法,这个名字代表的方法就是被overload了的。编译器会根据参
数类型列表的不同来决定调用叫这个名字的很多方法中具体的哪一个。指同样的东西在不同的地方具有多种含义。