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. JavaScript之对象序列化详解

    一.什么是对象序列化? 对象序列化是指将对象的状态转换为字符串(来自我这菜鸟的理解,好像有些书上也是这么说的,浅显易懂!): 序列化(Serialization)是将对象的状态信息转换为可以存储或传输 ...

  2. Eclipse UML小工具AmaterasUML的配置和使用

    AmaterasUML是个人认为最好用的Eclipse UML插件,能够通过拖拽Java源文件,轻松生成类图结构.同一时候支持活动图.时序图和用例图. 它的官方下载地址是:http://sourcef ...

  3. AspNet.WebAPI.OData.ODataPQ

    AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务 AspNet.WebAPI.OData.ODataPQ实现WebAPI的分页查询服务-(个人拙笔) AspNet. ...

  4. Android Wear 开发入门——怎样创建一个手机与可穿戴设备关联的通知(Notification)

    创建通知 为了创建在手机与可穿戴设备中都能展现的通知,能够使用 NotificationCompat.Builder.通过该类创建的通知,系统会处理该通知是否展如今手机或者穿戴设备中. 导入必要的类库 ...

  5. Android MediaPlayer 和 NativePlayer 播放格式控制

    对于本机MediaPlayer 支持格型式试验: 对于原生 NativeMedia 的支持格式測试: 这个支持就比較失望了,眼下測试的手机仅仅支持 H.264视频及AAC音频,其他的格式都不支持. 使 ...

  6. Linux中加入用户、删除用户时新手可能遇到的问题

    Linux中加入用户.删除用户时新手可能遇到的问题  1.创建新用户后切换到新用户:No directory, logging in with HOME=/     加入用户     #sudo us ...

  7. 解决方案命名空间“System.Web.Mvc”中不存在类型或命名空间名称“Ajax”(是否缺少程序集引用?)

    首先对System.Web.Mvc这个dll文件重新引用本地的,添加引用,搜索mvc就可以出来,选择相应的版本.如果还不能正常运行, 然后右键打开这个项目引用System.Web.Mvc, 将复制本地 ...

  8. poj 3273 Monthly Expense (二分)

    //最大值最小 //天数的a[i]值是固定的 不能改变顺序 # include <algorithm> # include <string.h> # include <s ...

  9. 快速构建Windows 8风格应用10-设备方向

    原文:快速构建Windows 8风格应用10-设备方向 本篇博文主要介绍常用支持Windows 8操作系统设备的方向.如何获取当前设备方向.DisplayProperties类. 常用支持Window ...

  10. 删除Python UserWarning[已解决]

    在使用MySQLdb包后,导入测试时发现一个警告. /usr/lib/python2.6/site-packages/setuptools-0.8-py2.6.egg/pkg_resources.py ...