C#虚方法
若一个实例方法声明前带有virtual关键字,那么这个方法就是虚方法。
虚方法与非虚方法的最大不同是,虚方法的实现可以由派生类所取代,这种取代是通过方法的重写实现的(以后再讲)
虚方法的特点:
虚方法前不允许有static,abstract,或override修饰符
虚方法不能是私有的,因此不能使用private修饰符
虚方法的执行:
我们知道一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,
而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,
其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。
如:A a =new B(); 其中A是申明类,B是实例类。
1.当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;
2.如果不是虚函数,那么它就直接执行该函数。而如果是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是开始检查对象的实例类。
3.在这个实例类里,他会检查这个实例类的定义中是否有实现该虚函数或者重新实现该虚函数(通过override关键字)的方法,
如果有,它就不会再找了,而是马上执行该实例类中实现的虚函数的方法。而如果没有的话,系统就会不停地往上找实例类的父类,
并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。
例1:

class A
{
public virtual void Sum()
{
Console.WriteLine("I am A Class,I am virtual sum().");
}
}
class Program
{
static void Main(string[] args)
{
A a=new A(); // 定义一个a这个A类的对象.这个A就是a的申明类,实例化a对象,A是a的实例类
a.Sum();
Console.Read();
}
}

执行a.Sum:
1.先检查申明类A 2.检查到是sum是虚拟方法 3.转去检查实例类A,结果是题本身
4.执行实例类A中实现Sum的方法 5.输出结果 I am A Class,I am virtual sum().
例2:

class A
{
public virtual void Sum()
{
Console.WriteLine("I am A Class,I am virtual sum().");
}
}
class B : A
{
public override void Sum() // 重新实现了虚函数
{
Console.WriteLine("I am B Class,I am override sum().");
} }
class Program
{
static void Main(string[] args)
{
A a=new B(); // 定义一个a这个A类的对象.这个A就是a的申明类,实例化a对象,B是a的实例类
a.Sum();
Console.Read();
}
}

执行a.Sum:
1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类B,有重写的方法 4.执行实例类B中的方法 5.输出结果 I am B Class,I am override sum().
例3:

class A
{
public virtual void Sum()
{
Console.WriteLine("I am A Class,I am virtual sum().");
}
}
class B : A
{
public override void Sum() // 重新实现了虚函数
{
Console.WriteLine("I am B Class,I am override sum().");
} }
class C : B
{ }
class Program
{
static void Main(string[] args)
{
A a=new C();// 定义一个a这个A类的对象.这个A就是a的申明类,实例化a对象,C是a的实例类
a.Sum();
Console.Read();
}
}

执行a.Sum:
1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类C,无重写的方法 4.转去检查类C的父类B,有重写的方法
5.执行父类B中的Sum方法 6.输出结果 I am B Class,I am override sum().
例4:

class A
{
public virtual void Sum()
{
Console.WriteLine("I am A Class,I am virtual sum().");
}
}
class B : A
{
public new void Sum() //覆盖父类里的同名函数,而不是重新实现
{
Console.WriteLine("I am B Class,I am new sum().");
} }
class Program
{
static void Main(string[] args)
{
A a=new B();
a.Sum();
Console.Read();
}
}

执行a.Sum:
1.先检查申明类A 2.检查到是虚拟方法 3.转去检查实例类B,无重写的(这个地方要注意了,虽然B里有实现Sum(),但没有使用override关键字,所以不会被认为是重写) 4.转去检查类B的父类A,就为本身 5.执行父类A中的Sum方法 6.输出结果 I am A Class,I am virtual sum().
那么如果在例4里,申明的是类B呢?

class A
{
public virtual void Sum()
{
Console.WriteLine("I am A Class,I am virtual sum().");
}
}
class B : A
{
public new void Sum() //覆盖父类里的同名函数,而不是重新实现
{
Console.WriteLine("I am B Class,I am new sum().");
} }
class Program
{
static void Main(string[] args)
{
B b=new B();
b.Sum();
Console.Read();
}
}

执行B类里的Sum(),输出结果I am B Class,I am new sum().
可以使用抽象函数重写基类中的虚函数吗?
答案是可以的。

class A
{
public virtual void PrintFriends()
{
Console.WriteLine("A.PrintFriends()");
}
}
abstract class B : A
{
public abstract override void PrintFriends(); //使用override 修饰符,表示抽象重写了基类中该函数的实现
}
abstract class C : A
{
public abstract new void PrintFriends(); //使用 new 修饰符显式声明,表示隐藏了基类中该函数的实现
}

密封类可以有虚函数吗?
可以,基类中的虚函数将隐式的转化为非虚函数,但密封类本身不能再增加新的虚函数

class A
{
public virtual void Fun()
{
Console.WriteLine("I am A.");
}
}
sealed class Program:A
{
public override void Fun()
{
Console.WriteLine("I am B.");
}
static void Main(string[] args)
{
Program p = new Program();
p.Fun();
Console.Read();
}
}

C#虚方法的更多相关文章
- C# 工厂模式+虚方法(接口、抽象方法)实现多态
面向对象语言的三大特征之一就是多态,听起来多态比较抽象,简而言之就是同一行为针对不同对象得到不同的结果,同一对象,在不同的环境下得到不同的状态. 实例说明: 业务需求:实现一个打开文件的控制台程序的d ...
- 重写ValidateEntity虚方法实现可控的上下文验证和自定义验证
上篇文章介绍了ValidationAttribute和IValidatableObject.Validate验证,但是这种验证还是稍微简单了,对于复杂的实体,例如:继承过来的实体.实现某接口的实体等等 ...
- Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息
Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息 http://www.cnblogs.com/zhwx/archive/2012/08/28/266055 ...
- 《转》 浅谈C# 多态的魅力(虚方法,抽象,接口实现)
前言:我们都知道面向对象的三大特性:封装,继承,多态.封装和继承对于初学者而言比较好理解,但要理解多态,尤其是深入理解,初学者往往存在有很多困惑,为什么这样就可以?有时候感觉很不可思议,由此,面向对象 ...
- C#属性-索引器-里氏替换-多态-虚方法-抽象-接口-泛型-
1.属性 //属性的2种写法 public class person { private string _name; public string Name { get { return _name; ...
- 为何JAVA虚函数(虚方法)会造成父类可以"访问"子类的假象?
首先,来看一个简单的JAVA类,Base. 1 public class Base { 2 String str = "Base string"; 3 protected vo ...
- C++虚方法(虚函数)随笔
本文不讨论虚函数的原理,只简单总结下虚函数的常用事项. 虚函数(虚方法)是C++动态联编 实现多态的重要手段,在函数声明时使用关键字virtual即可,如: virtual void func(voi ...
- 【jq】c#零基础学习之路(3)继承和虚方法
c#只能继承一个基类和多个接口(0+) 父类:Human: class Human { public virtual Move() { Console.WriteLine("Human的虚方 ...
- C#类和接口、虚方法和抽象方法及值类型和引用类型的区别
1.C#类和接口的区别接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!而类是负责功能的具体实现!在类中也有抽象类的定义,抽象类与接口的区别在于:抽象类是一个不完全的类,类里面有抽 ...
- [C#解惑] #1 在构造函数内调用虚方法
谜题 在C#中,用virtual关键字修饰的方法(属性.事件)称为虚方法(属性.事件),表示该方法可以由派生类重写(override).虚方法是.NET中的重要概念,可以说在某种程度上,虚方法使得多态 ...
随机推荐
- Diablo2 1.13版&PlugY10.00 男巫存档
下载地址: http://files.cnblogs.com/files/xiandedanteng/20160805D2113NanwuL83Backup.rar 解压后文件放到Diablo2游戏的 ...
- tomcat的maxThreads、acceptCount(最大线程数、最大排队数)
转载:http://blog.sina.com.cn/s/blog_605f5b4f01012ljj.html tomcat 的Connector配置如下 <Connector port=&qu ...
- iOS开发网络篇—简单介绍ASI框架的使用
iOS开发网络篇—简单介绍ASI框架的使用 说明:本文主要介绍网络编程中常用框架ASI的简单使用. 一.ASI简单介绍 ASI:全称是ASIHTTPRequest,外号“HTTP终结者”,功能十分强大 ...
- wkhtmltopdf 中文参数详解
linux:wkhtmltopdf [OPTIONS]… [More input files] windows:wkhtmltopdf.exe [OPTIONS]… [More input files ...
- leetcode105:Construct Binary Tree from Preorder and Inorder Traversal
题目: Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume t ...
- YL-64 颜色传感器
TCS3200颜色传感器是一款全彩的颜色检测器,包括了一块TAOS TCS3200RGB感应芯片和4个白光LED灯,TCS3200能在一定的范围内检测和测量几乎所有的可见光.它适合于色度计测量应用领域 ...
- 函数nvl 和decode
decode(nvl(kkc.category, 'one'),'one','普通','two','精品','three','行业','four','白金')
- Android DrawerLayout Plus 增强版抽屉菜单
DrawerLayout是官方提供的侧滑菜单,相比SliddingMenu,它更加轻量级.默认情况下,DrawerLayout可以设置左侧或者右侧滑出菜单.如下, xml布局: <!-- & ...
- java—数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = input数组中,除了input[i] 之外的所有数的乘积,不用考虑溢出例如 input {2, 3, 4, 5} output: {60, 40, 30, 24}
/** * 小米关于小米笔试题 数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = * input数组中,除了input[i] 之外的 ...
- String s ; 和 String s = null ; 和 String s = "" ; 的却别
String s ;该语句表示只是声明了一个引用变量,但是并没有初始化引用,所以对变量s的任何操作(除了初始化赋值外) 都将引发异常. String s=null; 表示未申请任何内存资源,即此语句表 ...