简介:继承、多态性、重载
本文通过整合MSDN和网上各种资料,写出的一些自己的心得体会
一、 继承
新类(即派生类)将获取基类的所有非私有数据和行为以及新类为自己定义的所有其他数据或行为。因此,新类具有两个有效类型:新类的类型和它继承的类的类型。
1、 继承C#中的类:C#不支持多重继承,C#类始终继承自一个基类(如果未在声明中指定一个基类,则继承自System.Object)。
派生类继承基类的语法如下: using System; public class Person { //这是基类 } public class Student:Person { //这是派生类 } 注:派生类继承基类时用“:”符号连接,派生类继承基类的所有非私有成员变量和方法。
2、 调用基类的构造函数:如果对类没有定义显式构造函数,则编译器将提供默认的构造函数,以初始化这些类的成员字段。但是,如果显式添加了构造函数,就可以有效控制整个构造过程。
|
示例: using System; namespace Jicheng { //基类 public class Person { public string _name; public uint _age; //基类的构造函数 public Person(string name,uint age) { this._name = name; this._age = age; //打印输出 Console.WriteLine(_name); Console.WriteLine(_age); } } //派生类 public class Student:Person { private uint _id; } //派生类构造函数并调用基类构造函数用“:base” public Student(string name,uint age uint id):base(name,age) { this._id = id; //打印输出 Console.WriteLine(_id); } } public class Exercise { [STAThread] static void Main(string[] args) { //构造 Student Student objstudent=new Student(“XYZ”,45,001); } } } 注:派生类通过使用:base()语法可以显式调用基类的构造函数。在必要的情况下,可以利用它来初始化字段。 |
二、 继承的调用顺序
1. 先调用构造函数,再调用其他类或者方法。有父类先调用父类,接着是子类以及孙类。
2. 有父子孙类,先调用父类,再调用子类、最后调用孙类。例如父类:A ;B:A ;C:B;调用C时,A、B的构造函数都要被调用。
三、 多态性
通过多态性,达到什么样一个目的?同一操作作用于不同类(基类以及其派生出来的任何类)实例,不同的类将进行不同的解释、最后产生不同的执行结果。而你不需要知道该对象属于哪个类。
定义:通过继承,一个类可以用作多种类型:可以用作它自己的类型、任何基类型,或者在实现接口时用作任何接口类型。这称为多态性。C# 中的每种类型都是多态的。类型可用作它们自己的类型或用作 Object 实例,因为任何类型都自动将 Object 当作基类型。
当派生类从基类继承时,它会获得基类的所有方法、字段、属性和事件。若要更改基类的数据和行为,您有两种选择:可以使用新的派生成员替换基成员,或者可以重写虚拟的基成员。
1. 使用新的派生成员替换基类的成员需要使用 new 关键字。如果基类定义了一个方法、字段或属性,则 new 关键字用于在派生类中创建该方法、字段或属性的新定义。new 关键字放置在要替换的类成员的返回类型之前
|
C# public class BaseClass { public void DoWork() { } public int WorkField; public int WorkProperty {get { return 0; }} } public class DerivedClass : BaseClass { public new void DoWork() { } public new int WorkField; public new int WorkProperty {get { return 0; }} } |
使用 new 关键字时,调用的是新的类成员而不是已被替换的基类成员。这些基类成员称为隐藏成员。如果将派生类的实例强制转换为基类的实例,就仍然可以调用隐藏类成员。例如:
|
C# DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Calls the old method. |
2. 为了使派生类的实例完全接替来自基类的类成员,基类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加 virtual 关键字来实现的。然后,派生类可以选择使用 override 关键字而不是 new,将基类实现替换为它自己的实现。例如:
|
C# public class BaseClass { public virtual void DoWork() { } public virtual int WorkProperty { get { return 0; } } } public class DerivedClass : BaseClass { public override void DoWork() { } public override int WorkProperty { get { return 0; } } } |
字段不能是虚拟的,只有方法、属性、事件和索引器才可以是虚拟的。当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问,也会调用该成员。例如:
|
C# DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Also calls the new method. |
使用虚拟方法和属性可以预先计划未来的扩展。由于在调用虚拟成员时不考虑调用方正在使用的类型,所以派生类可以选择完全更改基类的外观行为。
无论在派生类和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员都将永远为虚拟成员。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。例如:
|
C# public class A { public virtual void DoWork() { } } public class B : A { public override void DoWork() { } } |
|
C# public class C : B { public override void DoWork() { } } |
派生类可以通过将重写声明为密封的来停止虚拟继承。这需要在类成员声明中将 sealed 关键字放在 override 关键字的前面。例如:
|
C# public class C : B { public sealed override void DoWork() { } } |
在上面的示例中,方法 DoWork 对从 C 派生的任何类都不再是虚拟的。(意思就是它失去了重新的技能了!)它对 C 的实例仍然是虚拟的 -- 即使将这些实例强制转换为类型 B 或类型 A。派生类可以通过使用 new 关键字替换密封的方法,如下面的示例所示:
|
C# public class D : C { public new void DoWork() { }} |
在此情况下,如果在 D 中使用类型为 D 的变量调用 DoWork,被调用的将是新的 DoWork。如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork 的调用将遵循虚拟继承的规则,即把这些调用传送到类 C 的 DoWork 实现。
已替换或重写某个方法或属性的派生类仍然可以使用基关键字访问基类的该方法或属性。例如:
|
C# public class A { public virtual void DoWork() { } } public class B : A { public override void DoWork() { } } |
|
C # public class C : B { public override void DoWork() { // Call DoWork on B to get B's behavior: base.DoWork(); // DoWork behavior specific to C goes here: // ... } } |
四、 多态性的运用
大部分面向对象的编程系统都通过继承提供多态性。基于继承的多态性涉及在基类中定义方法并在派生类中使用新实现重写它们。
例如,可以定义一个基类 BaseTax,该类提供计算某个州/省的销售税的基准功能。从 BaseTax 派生的类(如 CountyTax 或 CityTax)可以根据相应的情况实现方法,如 CalculateTax。
多态性来自这样一个事实:可以调用属于从 BaseTax 派生的任何类的某个对象的 CalculateTax 方法,而不必知道该对象属于哪个类。
例如:
首先,创建一个名为 Shape 的基类,并创建一些派生类,例如 Rectangle、Circle 和 Triangle。为 Shape 类提供一个名为 Draw 的虚方法,并在每个派生类中重写该方法以绘制该类表示的特定形状。创建一个 List<Shape> 对象,并向该对象添加 Circle、Triangle 和 Rectangle。若要更新绘图图面,请使用 foreach 循环对该列表进行循环访问,并对其中的每个 Shape 对象调用 Draw 方法。虽然列表中的每个对象都具有声明类型 Shape,但调用的将是运行时类型(该方法在每个派生类中的重写版本)。
|
C# public class Shape {// A few example members public int X { get; private set; } public int Y { get; private set; } public int Height { get; set; } public int Width { get; set; } // Virtual method public virtual void Draw() {Console.WriteLine("Performing base class drawing tasks"); }}class Circle : Shape {public override void Draw() {// Code to draw a circle... Console.WriteLine("Drawing a circle"); base.Draw(); }}class Rectangle : Shape {public override void Draw() {// Code to draw a rectangle... Console.WriteLine("Drawing a rectangle"); base.Draw(); }}class Triangle : Shape {public override void Draw() {// Code to draw a triangle... Console.WriteLine("Drawing a triangle"); base.Draw(); }}class Program {static void Main(string[] args) {// Polymorphism at work #1: a Rectangle, Triangle and Circle // can all be used whereever a Shape is expected. No cast is // required because an implicit conversion exists from a derived // class to its base class. System.Collections.Generic.List<Shape> shapes = new System.Collections.Generic.List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Triangle()); shapes.Add(new Circle()); // Polymorphism at work #2: the virtual method Draw is // invoked on each of the derived classes, not the base class. foreach (Shape s in shapes) { s.Draw(); }// Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); }}/* Output: Drawing a rectanglePerforming base class drawing tasks Drawing a trianglePerforming base class drawing tasks Drawing a circlePerforming base class drawing tasks */ |
五、 多态性用到的几个概念
1. C#支持两种类型的多态性:
编译时的多态性(静态联编):编译时的多态性是通过重载来实现的。方法重载和操作符重载、它们都实现了编译时的多态性。对于非虚的成员来说系统在编译时根据传递的参数、返回的类型等信息决定实现何种操作。
运行时的多态性(动态联编)运行时的多态性就是指直到系统运行时才根据实际情况决定实现何种操作C#中运行时的多态性。通过虚成员实现。
编译时的多态性为我们提供了运行速度快的特点而运行时的多态性则带来了高度灵活和抽象的特点。
2. 重载:
重载是指具有相同名称的函数通过不同的参数个数、参数顺序、参数类型或者是返回值类型来区分。这样在调用的时候程序就可以通过识别参数和返回值,选择应该要调用的函数。完成方法灵活的复用。
特点(两必须一可以):方法名必须相同、参数列表必须不相同、返回值类型可以不相同
|
//举个例子 aq{p(Wc B0{V `R \]F)r0//不使用方法重载 public string GetString(string strValues) { if(strValues.Length==0) { return "今天我遇到无名了。。。。"; } return "Hello,"+strValues+"兄"; }
//使用重载就是 @d0public string GetString() { return GetString("我是无名。。。你是?"); }
H%JM#O C0public string GetString(string strValues) { return strValues; }
|
浙公网安备 33010602011771号