您的位置:首页 > 编程语言 > C#

C# 多态性

2015-06-11 08:02 337 查看
转载自:MSDN

类似文章:点击打开链接

Polymorphism(多态性)是一个希腊词,指“多种形态”,多态性具有两个截然不同的方面:

发生此情况时,该对象的声明类型不再与运行时类型相同。

在运行时,客户端代码调用该方法,CLR 查找对象的运行时类型,并调用虚方法的重写方法。 因此,你可以在源代码中调用基类的方法,但执行该方法的派生类版本。

派生类(子类)与基类(父类)之间强制转换不会丢失信息。

namespace PolymorphismTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ParentClass parent = new ParentClass();
SubClass sub = new SubClass();
parent = ((ParentClass)sub);
//子类 父类 实例之间来回转换不会丢失信息
SubClass subNew = (SubClass)parent;
}
}
public class SubClass : ParentClass
{
public new string name = "SubClass";
public int age = 20;
}
public class ParentClass
{
string name = "ParentClass";
}
}
执行到
SubClass subNew = (SubClass)parent
具体结果如下图:



例如,假定你有一个绘图应用程序,允许用户在绘图图面上创建各种形状。 你在编译时不知道用户将创建哪些特定类型的形状。 但应用程序必须跟踪创建的所有类型的形状,并且必须更新这些形状以响应用户鼠标操作。 你可以使用多态性通过两个基本步骤解决这一问题:

创建一个类层次结构,其中每个特定形状类均派生自一个公共基类。

使用虚方法通过对基类方法的单个调用来调用任何派生类上的相应方法。

为 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 rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/
在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object

关注对象原则:调用子类还是父类的方法,取决于创建的对象是子类对象还是父类对象

多态性概述

虚成员

派生类的设计器可以选择是否

重写基类中的虚拟成员。

继承最接近的基类方法而不重写它

定义隐藏基类实现的成员的新非虚实现

派生成员必须使用 override 关键字显式指示该方法将参与虚调用。 以下代码提供了一个示例:

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.
有关详细信息,请参阅使用 Override 和 New 关键字进行版本控制(C# 编程指南)。 接口提供另一种方式来定义将实现留给派生类的方法或方法集。 有关详细信息,请参阅接口(C# 编程指南)

使用新成员隐藏基类成员

new 关键字放置在要替换的类成员的返回类型之前。 以下代码提供了一个示例:

C#
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}

public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}
例如:

C#
DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

阻止派生类重写虚拟成员

如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。 以下代码提供了一个示例:

C#
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
这需要在类成员声明中的 override 关键字前面放置 sealed 关键字。 以下代码提供了一个示例:

C#
public class C : B
{
public sealed override void DoWork() { }
}
即使它们转换为类型 B 或类型 A,它对于 C 的实例仍然是虚拟的。 通过使用 new 关键字,密封的方法可以由派生类替换,如下面的示例所示:

C#
public class D : C
{
public new void DoWork() { }
}
如果使用类型为 C、B 或 A 的变量访问 D 的实例,对 DoWork 的调用将遵循虚拟继承的规则,即把这些调用传送到类 C 的 DoWork 实现。

从派生类访问基类虚拟成员

以下代码提供了一个示例:

C#
public class Base
{
public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Perform Derived's work here
//...
// Call DoWork on base class
base.DoWork();
}
}
有关详细信息,请参阅 base


说明
允许基类行为发生使得派生类能够集中精力实现特定于派生类的行为。 未调用基类实现时,由派生类负责使它们的行为与基类的行为兼容。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: