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();
}
}
c#中运行时编译时 多态的更多相关文章
- Android runProguard配置 导致module lib 中的包编译时无法识别
今天写代码时用到了另一个lib型的工程,把它添加到dependencies后,在原工程中可以引用lib中的文件了,但是编译时就会报错,提示包不存在,后来在build.gradle中设置runProgu ...
- 关于android源码中的APP编译时引用隐藏的API出现的问题
今天在编译android源码中的计算器APP时发现,竟然无法使用系统隐藏的API,比如android.os.ServiceManager中的API,引用这个类时提示错误,记忆中在android源码中的 ...
- Android中使用databinding编译时出现的error:Execution failed for task ':app:dataBindingProcessLayoutsDebug'
Windows环境下使用svn对AndroidStudio更新代码时,总会在源文件中出现一堆乱码,尤其是xml文件中的乱码,不仅找起来费劲,改起来更费劲. 最近从svn更新代码之后,编译时出现了下面这 ...
- keil 工程中多文件编译时全局变量怎么引用
由于代码较多时,为了代码的工整以及易读性,往往将代码拆分成模块,并书写头文件.但keil中定义全局变量往往是一件头疼的事情. (1)xx.h文件中基本书写的是管脚定义和函数声明,全局变量不能定义在头文 ...
- [经验总结] 在 windows 命令窗口中运行 python 脚本时提示 ModuleNotFoundError: No module named 'xxx'
先给出的代码和目录结构 获取CPU代码如下: # -*- coding:utf-8 -*- ''' Created on Sep 10, 2018 @author: ''' import sys im ...
- 解决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 ...
- 编译时和运行时、OC中对象的动态编译机制
编译时 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字 ...
- 《Effective C#》读书笔记-1.C# 语言习惯-2.使用运行时常量(readonly)而不是编译时常量(const)
概念 编译时 编译时顾名思义就是正在编译的时候.那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识 ...
- 运行javac编译报错:仅当显式请求注释处理时才接受类名称“xxxxxx”
发生原因:运行javac编译时没有加上扩展名.解决方法:加上.java扩展名重新编译即可,"xxxxxx.java".
随机推荐
- Eclipse 4.4.2 取消空格键代码上屏
基本是照这个站点来的: 点击打开链接 可是问题不止这些!! 从网上下了SDK, 然后打开之后首先发现没有plugins窗体, 好吧, 先通过Help -> Install New Softwar ...
- java一般使用基础
1.这是一个通用 仿制药javaA型安全机制来保护. 它是通过变量的类型,如果,实现变量类型限制,从而保证了程序编译变量类型的安全性在一定程度上.反之.如果没有泛型,这些只存在于程序,如果猿的大脑,这 ...
- C面试题
1.sizeof()和strlen()使用? 答案: 1.从功能定义,strlen功能,要查找字符串的长度,sizeof功能是用来寻找指定的变量或变量类型的存储器占用 尺寸: 2.sizeof是运算符 ...
- 用Maven整合SpringMVC+Spring+Hibernate 框架,实现简单的插入数据库数据功能
一.搭建開始前的准备 1.我用的MyEclipse2014版,大家也能够用IDEA. 2.下载Tomcat(免安装解压包).MySQL(zip包下载地址 免安装解压包,优点就是双击启动,最后我会把ba ...
- Yii Framework2.0开发教程(5)数据库mysql性能
继续<Yii Framework2.0开发教程(3)数据库mysql入门> 首先给予一定的尊重yii2数据库支持引进 Yii 基于 PHP's PDO一个成熟的数据库访问层的建立.它提供了 ...
- Java迭代器[转]
迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的.只要拿到这个对象,使用迭代器就可以遍历这个对象的内部. 1.Iterator ...
- hdu 逆袭指数
Problem Description 这依然是关于高富帅小明曾经的故事—— 尽管身处逆境,但小明一直没有放弃努力,除了搬砖,小明还研究过东方的八卦以及西方的星座,一直试图在命理上找到自己能够逆袭 ...
- centos7看电影
sudo rpm -Uvh https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm sudo rpm ...
- 修改 dll
由于没有源码,想改dll,就要依靠反汇编了. 输入 ildasm.exe 据说也可以直接 C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin 找到该软件 ...
- 平安某金所奇葩的面经-关于幂等和ROA设计的反思
在公司一直在做跟支付有关的项目,某日接到平安某金所一男子电话,应该是之前某猎头投的,我正好在吃早饭(也不能怪他们上班早,我们公司弹性工作制,我一般上班比较晚). 因为饭馆信号不好,只能赶紧放下剩下的半 ...