1)第一种情况:子类某个方法使用new修饰,但父类中并没有该方法。
Son类继承自Father,Father类中并没有SonSay()这个方法,在Son类中定义SonSay()方法,并且定义为new的,结果会怎么样呢?
运行结果:
Father.Say()
Son.SonSay()
即运行结果没问题,但编译器会提出一个警告:子类Son中的方法 SonSay()在父类中并不存在,没必要使用new关键字。
2)第二种情况:父类中有一个方法,子类中也有一个同样签名的方法,但父类中该方法没有使用使用virtual、override、abstract修饰,子类中该方法也没有使用new或者override修饰。
运行结果:
Father.Say()
Father.Say()
为什么 persons[1].Say()的运行结果也是Father.Say()呢?person[1]的实例类型明明是Son啊。因为在c#中,如果子类中的方法签名如果和父类中的方法签名一致而不是override的(名字相同,参数格式和类型也一致,跟返回值没关),那么就自动隐式的标记为new的方法了。而new出来的方法是不支持运行时绑定的,它是编译时绑定,即跟声明时类型有关,而跟运行时该对象的实际类型无关,即编译时已经确定是调用父类的还是子类的方法了。
另外编译器会给出一个警告:该方法签名跟父类的一样,如果是有意new的话,请加上new关键字。
3)第三种情况:子类的某个方法直接标识为override的,而父类该方法并没有声明为vitual、abstract或者override的。
这次运行结果会怎么样呢?编译器这次直接报错了,而不警告:
'NewVituslOverride.Son.Say()': cannot override inherited member 'NewVituslOverride.Father.Say()' because it is not marked virtual, abstract, or override。
编译错误内容很清楚,我就不解释了。
本例子说明override不是随便用的,必须父类的方法、属性、索引、事件是abstract、vitual或者override的时候,子类才能override它。
4)第四种情况:子类new父类中的方法。
运行结果:
Father.Say()
Father.Say()
如果第二种情况看懂了,就知道这个例子的运行结果和第二种情况一样的。只是这次代码用new把意思说的明明白白,子类我就是要隐藏父类的方法 Say() 。由于person[1]的声明类型是Father(虽然实际类型是Son),所以还是因为编译时绑定的原因,person[1].Say()跑出来的结果还是Father.Say()。
5)父类方法使用vitual,子类使用override重写
运行结果:
Father.Say()
Son.Say()
这次虽然 persons[1]的声明类型是Father,但因为Say()是vitual的,需要运行时根据对象的实际类型来调用,因为运行时person[1]的实际类型是Son,所以运行的是Son的Say()。
我们来仔细看下这个例子生成的IL代码:
Father类:
从里面可以看出Father类的Say()方法,被标识为newslot virtual的了。
我们接着来看Son类的IL代码:
从Son的IL代码中我们可以看出来,虽然我们在c#代码中声明Son类的Say()方法为override的,但实际上转换为IL代码后,该方法在Son类中是virtual的。
接着我们来看下Program类的IL代码:
构造函数就不用看了,主要是看Main函数及其里面的执行顺序。
在Main函数中,先声明了一个 Father[]数组,接着设置该数组的长度为2,接着创建了一个Father对象和一个Son对象。
关键是这两个对象调用Say()方法。我们可以看到是使用callvirl命令的,而不是call命令,使用callvirl命令就要求运行时检查对象的实际类型,然后调用该类型的对应方法。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- new public void SonSay()
- {
- Console.WriteLine("Son.SonSay()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father father = new Father();
- Son son = new Son();
- father.Say();
- son.SonSay();
- Console.ReadLine();
- }
- }
- }
运行结果:
Father.Say()
Son.SonSay()
即运行结果没问题,但编译器会提出一个警告:子类Son中的方法 SonSay()在父类中并不存在,没必要使用new关键字。
2)第二种情况:父类中有一个方法,子类中也有一个同样签名的方法,但父类中该方法没有使用使用virtual、override、abstract修饰,子类中该方法也没有使用new或者override修饰。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Father.Say()
为什么 persons[1].Say()的运行结果也是Father.Say()呢?person[1]的实例类型明明是Son啊。因为在c#中,如果子类中的方法签名如果和父类中的方法签名一致而不是override的(名字相同,参数格式和类型也一致,跟返回值没关),那么就自动隐式的标记为new的方法了。而new出来的方法是不支持运行时绑定的,它是编译时绑定,即跟声明时类型有关,而跟运行时该对象的实际类型无关,即编译时已经确定是调用父类的还是子类的方法了。
另外编译器会给出一个警告:该方法签名跟父类的一样,如果是有意new的话,请加上new关键字。
3)第三种情况:子类的某个方法直接标识为override的,而父类该方法并没有声明为vitual、abstract或者override的。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public override void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
'NewVituslOverride.Son.Say()': cannot override inherited member 'NewVituslOverride.Father.Say()' because it is not marked virtual, abstract, or override。
编译错误内容很清楚,我就不解释了。
本例子说明override不是随便用的,必须父类的方法、属性、索引、事件是abstract、vitual或者override的时候,子类才能override它。
4)第四种情况:子类new父类中的方法。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public new void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Father.Say()
如果第二种情况看懂了,就知道这个例子的运行结果和第二种情况一样的。只是这次代码用new把意思说的明明白白,子类我就是要隐藏父类的方法 Say() 。由于person[1]的声明类型是Father(虽然实际类型是Son),所以还是因为编译时绑定的原因,person[1].Say()跑出来的结果还是Father.Say()。
5)父类方法使用vitual,子类使用override重写
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public virtual void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public override void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Son.Say()
这次虽然 persons[1]的声明类型是Father,但因为Say()是vitual的,需要运行时根据对象的实际类型来调用,因为运行时person[1]的实际类型是Son,所以运行的是Son的Say()。
我们来仔细看下这个例子生成的IL代码:
Father类:
- .class public auto ansi beforefieldinit Father
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .method public hidebysig newslot virtual instance void Say() cil managed
- {
- .maxstack 8
- L_0000: nop
- L_0001: ldstr "Father.Say()"
- L_0006: call void [mscorlib]System.Console::WriteLine(string)
- L_000b: nop
- L_000c: ret
- }
- }
我们接着来看Son类的IL代码:
- .class public auto ansi beforefieldinit Son
- extends NewVituslOverride.Father
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void NewVituslOverride.Father::.ctor()
- L_0006: ret
- }
- .method public hidebysig virtual instance void Say() cil managed
- {
- .maxstack 8
- L_0000: nop
- L_0001: ldstr "Son.Say()"
- L_0006: call void [mscorlib]System.Console::WriteLine(string)
- L_000b: nop
- L_000c: ret
- }
- }
接着我们来看下Program类的IL代码:
- .class private auto ansi beforefieldinit Program
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .method private hidebysig static void Main(string[] args) cil managed
- {
- .entrypoint
- .maxstack 3
- .locals init (
- [0] class NewVituslOverride.Father[] persons)
- L_0000: nop
- L_0001: ldc.i4.2
- L_0002: newarr NewVituslOverride.Father
- L_0007: stloc.0
- L_0008: ldloc.0
- L_0009: ldc.i4.0
- L_000a: newobj instance void NewVituslOverride.Father::.ctor()
- L_000f: stelem.ref
- L_0010: ldloc.0
- L_0011: ldc.i4.1
- L_0012: newobj instance void NewVituslOverride.Son::.ctor()
- L_0017: stelem.ref
- L_0018: ldloc.0
- L_0019: ldc.i4.0
- L_001a: ldelem.ref
- L_001b: callvirt instance void NewVituslOverride.Father::Say()
- L_0020: nop
- L_0021: ldloc.0
- L_0022: ldc.i4.1
- L_0023: ldelem.ref
- L_0024: callvirt instance void NewVituslOverride.Father::Say()
- L_0029: nop
- L_002a: call string [mscorlib]System.Console::ReadLine()
- L_002f: pop
- L_0030: ret
- }
- }
在Main函数中,先声明了一个 Father[]数组,接着设置该数组的长度为2,接着创建了一个Father对象和一个Son对象。
关键是这两个对象调用Say()方法。我们可以看到是使用callvirl命令的,而不是call命令,使用callvirl命令就要求运行时检查对象的实际类型,然后调用该类型的对应方法。