Fork me on GitHub
c#中运行时编译时 多态

c#中运行时编译时 多态  

 

 

public class aa

{

}

public class bb:aa

{

}

public class cc

{

      public static void Main()

      {

 

}

 

 

多态性(Polymorphism)一词最早用于生物学,指同一种族的生物体具有相同的特性。

在C#中多态性的定义是:同一操作作用于不同的类的实例、不同的类将进行不同的解释、最后产生不同的执行结果。

C#支持两种类型的多态性:

编译时的多态性(静态联编)

编译时的多态性是通过重载来实现的。方法重载和操作符重载、它们都实现了编译时的多态性。

对于非虚的成员来说系统在编译时根据传递的参数、返回的类型等信息决定实现何种操作。

运行时的多态性(动态联编)

运行时的多态性就是指直到系统运行时才根据实际情况决定实现何种操作C#中运行时的多态性。

通过虚成员实现。

编译时的多态性为我们提供了运行速度快的特点而运行时的多态性则带来了高度灵活和抽象的特点。

虚方法

当类中的方法声明前加上了virtual 修饰符,我们称之为虚方法,反之为非虚。

使用了virtual 修饰符后不允许再有static, abstract, 或 override 修饰符。

对于非虚的方法,无论被其所在类的实例调用还是被这个类的派生类的实例调

用,方法的执行方式不变。而对于虚方法它的执行方式可以被派生类改变,这种改

变是通过方法的重载来实现的。

下面的例子说明了虚方法与非虚方法的区别:

程序清单

using System;

class A

{

public void F() { Console.WriteLine("A.F"); }

public virtual void G() { Console.WriteLine("A.G"); }

}

class B: A

{

new public void F() { Console.WriteLine("B.F"); }

public override void G() { Console.WriteLine("B.G"); }

}

class Test

{

static void Main() {

B b = new B();

A a = b;

a.F();

b.F();

a.G();

b.G();

}

}

例子中A 类提供了两个方法,非虚的F 和虚方法G 。类B 则提供了一个新的非虚的方法F,

 从而覆盖了继承的F 。类B 同时还重载了继承的方法G。 那么输出应该是

A.F

B.F

B.G

B.G

注意到本例中方法a.G() 实际调用了B.G 而不是A.G ,这是因为编译时值为A,但运行时值为B。

 所以B 完成了对方法的实际调用。

在派生类中对虚方法进行重载

先让我们回顾一下普通的方法重载。

普通的方法重载指的是类中两个以上的方法,包括隐藏的继承而来的方法,取的名字相同,

只要使用的参数类型或者参数个数不同,编译器便知道在何种情况下应该调用哪个方法。

而对基类虚方法的重载是函数重载的另一种特殊形式。

在派生类中重新定义此虚函数时要求的是方法名称,返回值类型,参数表中的参数个数,类型,顺序都必须

与基类中的虚函数完全一致。在派生类中声明对虚方法的重载要求在声明中加上override 关键字,

而且不能有new, static 或virtual 修饰符。

我们用汽车类的例子来说明多态性的实现

程序清单:

using System;

class Vehicle//定义汽车类

{

public int wheels; //公有成员轮子个数

protected float weight; //保护成员重量

public Vehicle(int w,float g){

wheels = w;

weight = g;

}

public virtual void Speak(){

Console.WriteLine(“the w vehicle is speaking!”);

}

};

class Car:Vehicle //定义轿车类

{

int passengers; //私有成员乘客数

public Car(int w,float g,int p) : base(w,g)

{

wheels = w;

weight = g;

passengers = p;

}

public override void Speak(){

Console.WriteLine(“The car is speaking:Di-di!”);

}

}

class Truck:Vehicle //定义卡车类

{

int passengers; //私有成员乘客数

float load; //私有成员载重量

public Truck (int w,float g,int p float l) : base(w,g)

{

wheels = w;

weight = g;

passengers = p;

load = l;

}

public override void Speak(){

Console.WriteLine(“The truck is speaking:Ba-ba!”);

}

}

class Test

{

public static void Main(){

Vehicle v1 = new Vehicle();

Car c1 = new Car(4,2,5);

Truck t1 = new Truck(6,5,3,10);

v1.Speak();

v1 = c1;

v1.Speak();

c1.Speak();

v1 = t1;

v1.Speak();

t1.Speak();

}

}

分析上面的例子我们看到

z Vehicle 类中的Speak 方法被声明为虚方法,那么在派生类中就可以重新定义

此方法;

z 在派生类Car 和Truck 中分别重载了Speak 方法。派生类中的方法原型和基类

中的方法原型必须完全一致;

z 在Test 类中创建了Vehicle 类的实例v1 ,并且先后指向Car 类的实例c1 和

Truck 类的实例t1。

运行该程序结果应该是

The Vehicle is speaking!

The car is speaking:Di-di!

The car is speaking:Di-di!

The truck is speaking:Ba-ba!

The truck is speaking:Ba-ba!

这里Vehicle 类的实例v1 先后被赋予Car 类的实例c1 以及Truck 类的实例t1的值。

在执行过程中,v1 先后指代不同的类的实例从而调用不同的版本。

这里v1 的Speak 方法实现了多态性,并且v1.Speak 究竟执行哪个版本不是在程序编译时确定的

而是在程序的动态运行时根据v1 某一时刻的指代类型来确定的,所以还体现了动态的多态性。

exercise:求输入结果。

public class A 

{

        public virtual void Fun1(int i)

       {

            Console.WriteLine(i);

        }

        public void Fun2(A a)  

       {

           //Console.WriteLine(this.ToString());

            a.Fun1(1);

            Fun1(5);

        }

}

public class B : A 

{

        public override void Fun1(int i)   

       {

            base.Fun1 (i + 1);

        }

        public static void Main()  

       {

            B b = new B();

            A a = new A();

            a.Fun2(b);

            b.Fun2(a);         

        }

}

Answer:

/*

    2

    5

    1

    6

*/

 

 

今天在看C#参考时明白了 什么是C#的运行时类型与编译时类型,记录下来,以备查阅:

相信很多刚学C#的人都对这两个概念感到迷惑,其时并不难(以前太重于技术可用性,比如:asp.net中的DataDrid怎么用等问题,没大研究过这方面),希望能对C#初学者有所帮助。请看如下代码:

class A

{...}

class B

{...}

class App

{

   public static void Main()

   {

        A a = new A(); //实例化一个A的对象a

        B b = a; //把B的对象b指向a

        ......

   }

}

以上代码中的对象a没什么好说的,运行时与编译时类型均为A

而对象b的运行时类型为A,编译时类型为B

说说原因,因为对象a,b本身都是引用类型,在编译时对象b的类型由我们写的B b来确定,而在程序运行后发现b引用的内容实际是a所引用的内容,而对象a的类型是确定为A的(因已实例化了),所以b 的运行时类型就和a的类型相同了

 

using System;
class A
{
    public void Fun()
    {
        Console.WriteLine(this.GetType());

/因为say是实例方法,/编译时就已经确定了

        this.Say();
    }
    public void Say()
    {
        Console.WriteLine("In A");
    }
}
class B : A
{
    public   void Say()
    {
        Console.WriteLine("In B");
    }
}
class tst
{
    static void Main()
    {
        B a = new B();
        a.Fun();
    }
}

换成虚方法

using System;
class A
{
    public void Fun()
    {
        Console.WriteLine(this.GetType());
        this.Say();
    }
    public virtual void Say()
    {
        Console.WriteLine("In A");
    }
}
class B : A
{
    public  override void Say()
    {
        Console.WriteLine("In B");
    }
}
class tst
{
    static void Main()
    {
        B a = new B();
        a.Fun();
    }
}

posted on 2014-02-28 00:11  HackerVirus  阅读(866)  评论(0编辑  收藏  举报