1. 多态性:
概念:拥有共同基类的派生类,从基类中继承的方法,都有不同的实现方式。
多态性可以简单地概括为"一个接口,多种方法",它是在程序运行的过程中才决定调用的方法,多态性是面向对象编程的核心概念。多态使得子类的实例可以直接赋予基类的对象,然后直接就可以通过这个对象调用子类(派生类)的方法。
注:1、多态性主要用于实现接口重用,因为接口是程序中最耗费时间的资源,实质上设计一个接口要比设计一堆类要显得更有效率。2、多态性在C#中主要通过虚方法和重写方法来体现。
a) 抽象类
抽象方法:只有方法的声明,而没有具体的实现。
含有抽象方法的类,称之为抽象类。有抽象方法的类一定是抽象类,抽象类例不一定有抽象方法。抽象类或抽象方法都用到了关键字:abstract
对于抽象类来说,只能作为其他类的基类(不能创建对象),而对于派生类来说,如果派生类不是抽象类的话,那么要求派生类必须重写(关键字override)基类的所有抽象方法。在抽象类中可以含有已经实现的方法,不能创建对象。
b) 在派生类中使用new 隐藏基类的方法。
如果基类不是抽象类,而是含有一个具体实现方法的类,则在派生类中,通过使用new关键字来实现多态。
c) 基类是虚方法(virtual),派生类重写(override)基类的方法,注意:方法的重载的区别。
d) 重载:同一个类中,方法的名字完全一样,但是参数不一样,包括:1.参数的个数不一样,2。参数的数据类型不一样
注:如果C#根据调用参数,没有找到对应的方法,那么将调用最近类型参数的方法。例:
public class TestAdd
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(string a, string b)
{
return Convert.ToInt32(a) + Convert.ToInt32(b);
}
}
class Program
{
static void Main(string[] args)
{
TestAdd t = new TestAdd();
t.Add(0, 2);//调用Add(int a,int b)
t.Add(2.0, 3.0);//调用Add(double a, double b)
t.Add("2", "5");//调用Add(string a, string b)
t.Add(2, 3.0);//调用Add(double a, double b)
}
}
多态举例:覆盖 在子类中用 new 关键字修饰 定义的与父类中同名的方法,叫覆盖。覆盖不会改变父类方法的功能。 看下面演示代码:例1、
public class BaseClass
{
public void Say()
{
Console.WriteLine("BaseClass");
}
}
public class DriveClass : BaseClass
{
public new void Say()//使用关键字new隐藏基类同名的方法
{
Console.WriteLine("Drive Class");
}
}
class Program
{
static void Main(string[] args)
{
BaseClass dc = new DriveClass();//谁引用调用谁
DriveClass dd = new DriveClass();
dc.Say();
dd.Say();
}
}
输出结果:
BaseClass
DriveClass
重写:用关键字 virtual 修饰的方法,叫虚方法。可以在子类中用override 声明同名的方法,这叫"重写"。相应的没有用virtual修饰的方法,我们叫它实方法。重写会改变父类方法的功能。看下面演示代码:例2、
public class BaseClass
{
public virtual void Say()
{
Console.WriteLine("BaseClass");
}
}
public class DriveClass : BaseClass
{
public override void Say()//使用关键字new隐藏基类同名的方法
{
Console.WriteLine("Drive Class");
}
}
class Program
{
static void Main(string[] args)
{
BaseClass dc = new DriveClass();//谁引用调用谁
DriveClass dd = new DriveClass();
dc.Say();
dd.Say();
}
}
输出结果:
DriveClass
DriveClass
总结1:不管是重写还是覆盖都不会影响父类自身的功能(废话,肯定的嘛,除非代码被改)。2:当用子类创建父类的时候,如 C1 c3 = new C2(),重写会改变父类的功能,即调用子类的功能;而覆盖不会,仍然调用父类功能。3:虚方法、实方法都可以被覆盖(new),抽象方法,接口 不可以。4:抽象方法,接口,标记为virtual的方法可以被重写(override),实方法不可以。5:重写使用的频率比较高,实现多态;覆盖用的频率比较低,用于对以前无法修改的类进行继承的时候。
例3、通过类的多态性确定人类的说话行为
public partial class Form1 : Form
{
public Form1()
{ InitializeComponent();}
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text == "")//判断是否输入了姓名
{
Console.WriteLine("请输入姓名:");
return;
}
richTextBox1.Clear();//清空文本框内容
string strName = textBox1.Text;//记录用户输入的名字
People[] people = new People[2];//声明People类型数组
people[0] = new Chinese();//使用第一个派生类对象初始化数组的第一个元素
people[1] = new American();//使用第二个派生类对象初始化数组的第二个元素
for (int i = 0; i < people.Length; i++)//遍历赋值后的数组
{//根据数组元素调用相应派生类中的重写方法
people[i].Say(richTextBox1,strName);
}
}
}
class People//定义基类
{//定义一个虚方法,用来表示人的说话行为
public virtual void Say(RichTextBox rtbox, string name)
{
rtbox.Text += name;//输出人的名字
}
}
class Chinese : People//定义派生类,继承于People类
{//重写基类中的虚方法
public override void Say(RichTextBox rtbox, string name)
{
base.Say(rtbox, name + "说汉语!");
}
}
class American : People//定义派生类,继承于People类
{//重写基类中的虚方法
public override void Say(RichTextBox rtbox, string name)
{
base.Say(rtbox, name + "说英语!");
}
}
base其实最大的使用地方在面相对性开发的多态性上,base可以完成创建派生类实例时调用其基类构造函数或者调用基类上已被其他方法重写的方法。例如:1关于base调用基类构造函数public class A{ public A() { Console.WriteLine("Build A");} }public class B:A{ public B():base() { Console.WriteLine("Build B"); }
static void Main() { B b = new B(); Console.ReadLine(); }}创建一个B的实例对象,获得结果是同时打印Build A和Build B.2关于base在派生类中调用基类的方法。public class A{ public virtual void Hello() { Console.WiriteLine("Hello");}}public class B : A{ public override void Hello() { base.Hello();//调用基类的方法,显示Hello Console.WiriteLine("World"); }}这样如果程序调用B.Hello()获得的效果将会使Hello World.最后补充下,根据MSDN Library介绍来看这两个关键字都是属于[访问关键字]类型
关于base
base 关键字用于从派生类中访问基类的成员: 调用基类上已被其他方法重写的方法。 指定创建派生类实例时应调用的基类构造函数。 基类访问只能在构造函数、实例方法或实例属性访问器中进行。 示例: 1. 在派生类中调用基类方法。
using System;public class BaseClass{ protected string _className = "BaseClass"; public virtual void PrintName() { Console.WriteLine("Class Name: {0}", _className); }}class DerivedClass : BaseClass{ public string _className = "DerivedClass"; public override void PrintName() { Console.Write("The BaseClass Name is {0}"); //调用基类方法 base.PrintName(); Console.WriteLine("This DerivedClass is {0}", _className); }}class TestApp{ public static void Main() { DerivedClass dc = new DerivedClass(); dc.PrintName(); }}
2. 在派生类中调用基类构造函数。 // keywords_base2.cs using System;public class BaseClass{ int num; public BaseClass() { Console.WriteLine("in BaseClass()"); } public BaseClass(int i) { num = i; Console.WriteLine("in BaseClass(int {0})", num); }}public class DerivedClass : BaseClass{ // 该构造器调用 BaseClass.BaseClass() public DerivedClass() : base() { } // 该构造器调用 BaseClass.BaseClass(int i) public DerivedClass(int i) : base(i) { } static void Main() { DerivedClass dc = new DerivedClass(); DerivedClass dc1 = new DerivedClass(1)(); Console.ReadLine(); }}
注意: 从静态方法中使用 base 关键字是错误的。 base 主要用于面向对象开发的多态这方面,在示例2中有体现。
关于this
this 关键字引用类的当前实例。 以下是 this 的常用用途: 限定被相似的名称隐藏的成员 将对象作为参数传递到其他方法 声明索引器 示例: // this 关键字 // keywords_this.cs using System;class Employee{ private string _name; private int _age; private string[] _arr = new string[5]; public Employee(string name, int age) { // 使用this限定字段,name与age this._name = name; this._age = age; } public string Name { get { return this._name; } } public int Age { get { return this._age; } } // 打印雇员资料 public void PrintEmployee() { // 将Employee对象作为参数传递到DoPrint方法 Print.DoPrint(this); } // 声明索引器 public string this[int param] { get { return _arr[param]; } set { _arr[param] = value; } }}class Print{ public static void DoPrint(Employee e) { Console.WriteLine("Name: {0}Age: {1}", e.Name, e.Age); }}class TestApp{ static void Main() { Employee E = new Employee("Hunts", 21); E[0] = "Scott"; E[1] = "Leigh"; E[4] = "Kiwis"; E.PrintEmployee(); for (int i = 0; i < 5; i++) { Console.WriteLine("Friends Name: {0}", E[i]); } Console.ReadLine(); }}