面向对象-多态
Posted zoupengfei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象-多态相关的知识,希望对你有一定的参考价值。
多态
通过继承,一个类型可以用作多种类型,可以用作自己的类型,任何基类类型,或者在实现接口时用作任何接口类型称多态。
在C#中所有引用类型自动将 System.Object 类型作为基类,值类型自动将 System.ValueType 类型作为基类。
代码:
1 /// <summary> 2 /// 基类接口,定义行为。 3 /// </summary> 4 public interface IPeople 5 { 6 void Walk(); 7 } 8 9 /// <summary> 10 /// 基类 11 /// </summary> 12 public class People: IPeople 13 { 14 public int _age { get; set; } 15 public string _name { get; set; } 16 public People(string name, int age = 1) 17 { 18 _age = age; 19 _name = name; 20 } 21 public void Walk() 22 { 23 Console.WriteLine($"{_name} will walk."); 24 } 25 26 public void Introduction() 27 { 28 Console.WriteLine($" name:{_name} age:{_age} Introduction."); 29 } 30 31 public void Specialty() 32 { 33 Console.WriteLine($" name:{_name} age:{_age} Specialty."); 34 } 35 } 36 37 /// <summary> 38 /// 派生自People 39 /// </summary> 40 public class Man : People 41 { 42 public Man(string _name, int _age) : base(_name, _age) 43 { 44 } 45 } 46 47 /// <summary> 48 /// 派生自People 49 /// </summary> 50 public class Woman : People 51 { 52 public Woman(string _name, int _age) : base(_name, _age) 53 { 54 } 55 }
1 static void Main(string[] args) 2 { 3 People xiaoming = new Man("xiaoming",20); 4 xiaoming.Walk(); 5 6 People xiaohong = new Woman("xiaohong", 19); 7 xiaohong.Walk(); 8 9 IPeople xiaowang = new Woman("xiaowang", 19); 10 xiaohong.Walk(); 11 }
上述代码中 People 类既能用作于 Man 类和 Woman 类,People类继承实现 IPeople 接口,IPeople 也可用作 Man 类和 Woman 类。多态不仅对派生类很重要,对基类也很重要。使用基类实际上都可能是在使用已强制转换为基类类型的派生类对象。基类的设计者可能预测到其基类中可能会在派生类中发生更改的地方。
多态概念:
当派生类从基类继承时,它会获取基类的所有方法,字段,属性和事件面向对象语言使用虚方法来表示多态。若要改变基类数据和行为。你有两种方法选择:可以使用新的派生成员替换基类成员,或者重写虚方法。
使用新的派生成员替换基类成员需要使用 new 关键字。如果基类定义了一个方法,字段或者属性,则 new 关键字用于在派生类中创建该方法,字段和属性的新定义。new 关键字应该放在替换成员的返回类型的前面。
代码:
1 /// <summary> 2 /// 基类 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public new int _age { get; set; } 25 public new string _name { get; set; } 26 27 public Man(string _name, int _age) : base(_name, _age) 28 { 29 } 30 31 public new void Walk() 32 { 33 Console.WriteLine($"{_name} will walk."); 34 } 35 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Man man = new Man("xiaoming", 20); 6 man.Walk(); 7 } 8 }
派生类使用 new 关键字对基类的成员重写定义,调用的是派生类的新成员而不是基类的成员,此时的基类成员已经被派生类的新成员替代了。此时基类成员称为隐藏成员。这里需要注意的是如果将派生类型实例强制转换为基类的实例,就仍然调用的基类的成员。
代码:
1 static void Main(string[] args) 2 { 3 Man man = new Man("xiaoming", 20); 4 man.Walk();// 调用的新方法 5 6 People people = (People)man; 7 people.Walk();// 调用的旧方法 8 }
为了派生类的实例完全替换掉基类的成员,基类必须将该成员声明为虚拟的。声明虚拟的成员时在成员的返回类型前面添加 virtual 关键字而不是 new 关键字。
代码:
1 /// <summary> 2 /// 基类 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public override int _age { get; set; } 25 public override string _name { get; set; } 26 public Man(string _name, int _age) : base(_name, _age) 27 { 28 } 29 30 public override void Walk() 31 { 32 Console.WriteLine($"{_name} will walk."); 33 } 34 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Man man = new Man("xiaoming", 20); 6 man.Walk();// 调用的新方法 7 8 People people = (People)man; 9 people.Walk();// 调用的新方法 10 11 } 12 }
需要注意的是字段是不能为虚拟的,只有方法,属性,事件和索引器可以是虚拟的。当派生类中重写基类成员后时,即使派生类实例强制转换为基类的实例时,调用的还时派生类的新成员。
使用虚拟方法和属性可以预先计划未来的扩展。由于在调用虚拟成员时不考虑调用方正在使用的类型,所以派生类可以选择完全更改基类的外观行为。无论在派生类和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员都将永远为虚拟成员。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。
代码:
1
派生类可以通过将重写声明为密封的来停止虚拟继承。这需要在类成员声明中将 sealed 关键字放在 override 关键字的前面
代码:
1 /// <summary> 2 /// 基类 3 /// </summary> 4 public class People : IPeople 5 { 6 public virtual int _age { get; set; } 7 public virtual string _name { get; set; } 8 public People(string name, int age = 1) 9 { 10 _age = age; 11 _name = name; 12 } 13 public virtual void Walk() 14 { 15 Console.WriteLine($"{_name} will walk."); 16 } 17 } 18 19 /// <summary> 20 /// 派生自People 21 /// </summary> 22 public class Man : People 23 { 24 public override int _age { get; set; } 25 public override string _name { get; set; } 26 public Man(string _name, int _age) : base(_name, _age) 27 { 28 } 29 30 public override void Walk() 31 { 32 Console.WriteLine($"{_name} will walk."); 33 } 34 } 35 36 public class ChinesePeople : Man 37 { 38 public override int _age { get; set; } 39 public override string _name { get; set; } 40 public ChinesePeople(string _name, int _age) : base(_name, _age) 41 { 42 } 43 44 public override void Walk() 45 { 46 Console.WriteLine($"{_name} will walk."); 47 } 48 }
派生类可以通过将重写声明为密封的来停止虚拟继承。这需要在类成员声明中将 sealed 关键字放在 override 关键字的前面
代码:
1 public class ChinesePeople : Man 2 { 3 public sealed override int _age { get; set; } 4 public sealed override string _name { get; set; } 5 public ChinesePeople(string _name, int _age) : base(_name, _age) 6 { 7 } 8 9 public sealed override void Walk() 10 { 11 Console.WriteLine($"{_name} will walk."); 12 } 13 }
在上面的代码中,方法 Walk 从 ChinesePeople 派生的任何类都不再是虚拟的。它对 ChinesePeople 的实例仍然是虚拟的 ,即使将这些实例强制转换为类型 Man 或类型 People 。派生类可以通过使用 new 关键字替换密封的方法。
代码:
1 public class SZPeople : ChinesePeople 2 { 3 public new int _age { get; set; } 4 public new string _name { get; set; } 5 public SZPeople(string _name, int _age) : base(_name, _age) 6 { 7 } 8 9 public new void Walk() 10 { 11 Console.WriteLine($"{_name} will walk."); 12 } 13 }
在此情况下,如果在 SZPeople 中使用类型为 SZPeople 的变量调用 Walk ,被调用的是新的 Walk ,也就是 SZPeople的新成员。如果使用类型为 ChinesePeople、Man 或 People 的变量访问 ChinesePeople 的实例,对 Walk 的调用将遵循虚拟继承的规则,即把这些调用传送到类 Man 的 Walk 实现。已替换或重写某个方法或属性的派生类仍然可以使用基关键字访问基类的该方法或属性。
注意建议虚拟成员在它们自己的实现中使用 base 来调用该成员的基类实现。允许基类行为发生使得派生类能够集中精力实现特定于派生类的行为。未调用基类实现时,由派生类负责使它们的行为与基类的行为兼容。
以上是关于面向对象-多态的主要内容,如果未能解决你的问题,请参考以下文章