-
C#教程之C#面向对象--多态
试听地址 https://www.xin3721.com/eschool/CSharpxin3721/
一、对于继承自同一基类的多个不同派生类的实例,在运行时可以将它们赋值给基类类型的变量,访问该变量的同一个成员会根据该变量运行时类型的不同而产生不同的行为,这个特性即被称为多态(Polymorphism);多态允许以同一种方式访问同一个变量的成员而产生不同的行为;
1.在C#中,每个类型都是多态的,因为包括自定义类型在内的所有类型都继承自基类System.Object;
2.虚成员、抽象成员和重写成员、替换成员是多态的基础;
3.非抽象基类中可以将方法、属性、事件和索引器声明为虚成员并实现,抽象基类中可以将这些成员声明为抽象成员而不实现,派生类中可以通过override关键字重写这些虚成员或抽象成员提供差异化实现,这样会代替基类中的虚成员,此时无论在代码中调用编译时类型为基类或派生类的变量的成员,在运行时CLR都会根据变量的运行时类型是基类或派生类来判断调用基类中的原成员或派生类中的重写成员;
4.如果不想让基类中的虚成员被代替,则应该在派生类中通过new关键字隐藏此虚成员并提供新的实现,此时在代码中调用编译时类型为基类的变量的成员,在运行时CLR会调用基类中该方法的实现;在代码中调用编译时类型为派生类的变量的成员,在运行时CLR会调用派生类中该方法的实现;
public class Animal { public virtual void Call() { Console.WriteLine("call~"); } } public class Cat : Animal { //使用override重写基类虚方法时,转换为基类变量后调用的为派生类中的重写方法 public override void Call() { Console.WriteLine("miao~"); } } public class Dog : Animal { //使用new隐藏基类虚方法时,转换为基类变量后调用的为基类中的虚方法 public new void Call() { Console.WriteLine("wang~"); } }
使用方式:
new Cat().Call(); //miao~ new Dog().Call(); //wang~ List<Animal> animals = new List<Animal>() { new Cat(), new Dog() }; //此处使用了隐式转换,将派生类对象隐式转换为基类变量 for (int i = 0; i < animals.Count; i++) { animals[i].Call(); //miao~ call~ }
5.通过隐式转换将派生类实例赋值给基类变量,再将该基类变量引用的对象通过显式转换或as运算符转换后赋值给派生类变量,此时该派生类变量引用的对象的所有数据和行为较最初的派生类实例相比不会发生任何改变,实际上,类的实例在类型转换过程中其类型不会发生任何改变,只是在调用过程中会根据引用该实例的变量的编译时类型执行不同的调用方式,会优先查找实例中变量的编译时类型所对应的实现;
class BaseClass { public virtual void Method1() { Console.WriteLine("Base_Method1"); } public virtual void Method2() { Console.WriteLine("Base_Method2"); } } class DerivedClass : BaseClass { public int MyNum; public override void Method1() { Console.WriteLine("Derived_Method1"); } public new void Method2() { Console.WriteLine("Derived_Method2"); } }
使用方式:
DerivedClass derivedObj = new DerivedClass() { MyNum = 1 }; BaseClass baseObj = new BaseClass(); Console.WriteLine(derivedObj is BaseClass); //true Console.WriteLine(baseObj is DerivedClass); //false derivedObj.Method1(); //Derived_Method1 derivedObj.Method2(); //Derived_Method2 //将派生类对象隐式转换为基类对象 baseObj = derivedObj; //此时baseObj中存的依然是原来的DerivedClass的实例,通过反射即可得知 Type myType = baseObj.GetType(); Console.WriteLine(myType.Name); //DerivedClass Console.WriteLine(myType.GetField("MyNum").GetValue(baseObj)); //1 myType.GetMethod("Method1").Invoke(baseObj, null); //Derived_Method1 myType.GetMethod("Method2").Invoke(baseObj, null); //Derived_Method2,通过反射获取方法信息数组时会发现有两个Method2,但这么调用只会调用派生类中的Method2 //通过baseObj直接访问 Console.WriteLine(baseObj is DerivedClass); //true baseObj.Method1(); //Derived_Method1 baseObj.Method2(); //Base_Method2 //将上面转换后的基类变量引用的对象再次转换为派生类对象,其原始数据不变 DerivedClass derivedObj2 = baseObj as DerivedClass; derivedObj2.Method1(); //Derived_Method1 derivedObj2.Method2(); //Derived_Method2 Console.WriteLine(derivedObj2.MyNum); //1
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!
作者:Minotauros
出处:https://www.cnblogs.com/minotauros/