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();
    }
}

c#中运行时编译时 多态的更多相关文章

  1. Android runProguard配置 导致module lib 中的包编译时无法识别

    今天写代码时用到了另一个lib型的工程,把它添加到dependencies后,在原工程中可以引用lib中的文件了,但是编译时就会报错,提示包不存在,后来在build.gradle中设置runProgu ...

  2. 关于android源码中的APP编译时引用隐藏的API出现的问题

    今天在编译android源码中的计算器APP时发现,竟然无法使用系统隐藏的API,比如android.os.ServiceManager中的API,引用这个类时提示错误,记忆中在android源码中的 ...

  3. Android中使用databinding编译时出现的error:Execution failed for task ':app:dataBindingProcessLayoutsDebug'

    Windows环境下使用svn对AndroidStudio更新代码时,总会在源文件中出现一堆乱码,尤其是xml文件中的乱码,不仅找起来费劲,改起来更费劲. 最近从svn更新代码之后,编译时出现了下面这 ...

  4. keil 工程中多文件编译时全局变量怎么引用

    由于代码较多时,为了代码的工整以及易读性,往往将代码拆分成模块,并书写头文件.但keil中定义全局变量往往是一件头疼的事情. (1)xx.h文件中基本书写的是管脚定义和函数声明,全局变量不能定义在头文 ...

  5. [经验总结] 在 windows 命令窗口中运行 python 脚本时提示 ModuleNotFoundError: No module named 'xxx'

    先给出的代码和目录结构 获取CPU代码如下: # -*- coding:utf-8 -*- ''' Created on Sep 10, 2018 @author: ''' import sys im ...

  6. 解决eclipse中运行web项目时弹出的"Port 8080 required by Tomcat 9.0 Server at localhost is already in use...

    1.tomcat默认端口是8080,可以修改通过tomcat的端口 修改tomcat\conf\server.xml     结果运行程序,还是报"Port 8080 required by ...

  7. 编译时和运行时、OC中对象的动态编译机制

    编译时 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字 ...

  8. 《Effective C#》读书笔记-1.C# 语言习惯-2.使用运行时常量(readonly)而不是编译时常量(const)

    概念 编译时 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识 ...

  9. 运行javac编译报错:仅当显式请求注释处理时才接受类名称“xxxxxx”

    发生原因:运行javac编译时没有加上扩展名.解决方法:加上.java扩展名重新编译即可,"xxxxxx.java".

随机推荐

  1. POJ 3692:Kindergarten(最大的使命)

    id=3692">Kindergarten Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4920   Ac ...

  2. 【Linux】Vim编辑器-批量注释与反注释

    [-] vim编辑器---批量注释与反注释 方法一 块选择模式 插入注释 取消注释 方法二 替换命令 批量注释 取消注释 实例演示   vim编辑器---批量注释与反注释 在使用vim编写代码的时候, ...

  3. UVA Don't Get Rooked

    主题如以下:  Don't Get Rooked  In chess, the rook is a piece that can move any number of squaresverticall ...

  4. 手游开发者大会交流OGEngine新版本发布

    由OGEngine第二个手游开发者交流会举办的圆桌会议在深圳市高新技术园区举行.发布会不仅吸引了手游开发商,供应商也纷纷在国外支付,在国内手游和国外出版商参加. 围绕三个主题会议讨论和交流.每个主题: ...

  5. 如何从 0 开始学 Ruby on Rails

    如何从 0 开始学 Ruby on Rails (漫步版)Ruby 是一门编程语言,Ruby on Rails 是 Ruby 的一个 web 框架,简称 Rails. 有很多人对 Rails 感兴趣, ...

  6. 在Windows系统中安装集成的PHP开发环境

    原文:在Windows系统中安装集成的PHP开发环境 刚想学php的,又不会配置复杂php的环境,可以使用集成的,目前网上提供常用的PHP集成环境主要有AppServ.phpStudy.WAMP和XA ...

  7. Codeforces 461B Appleman and Tree(木dp)

    题目链接:Codeforces 461B Appleman and Tree 题目大意:一棵树,以0节点为根节点,给定每一个节点的父亲节点,以及每一个点的颜色(0表示白色,1表示黑色),切断这棵树的k ...

  8. socket在windows下和linux下的区别

    原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h    错误处理:errno.h 2 ...

  9. PHP 生成唯一激活码

    <?php /** * 从来没有产生一个唯一的激活码 * @return string */ function create_guid($namespace = null) { static $ ...

  10. curl_redir_exec()函数

    function curl_redir_exec($ch,$debug="") { static $curl_loops = 0; static $curl_max_loops = ...