C#之虚函数

摘要:
如果它是一个虚拟函数,它不会立即执行该函数,而是开始检查对象的实例类。classA{publicvirtualvoidSum(){Console.WriteLine;}}ClassB:A{publicnewvoidSum()//覆盖父类中同名的函数,而不是重新实现{Console.WriteLine;}}classProgram{staticvoidMain{Bb=newB();b.Sum();Console.Read();}}在类B中执行Sum(),并输出IamBClass、Iamnewsum()。您可以使用抽象函数重写基类中的虚拟函数吗?

若一个实例方法声明前带有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#之虚函数》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇React和Jquery比较Oracle-SQL 建表下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

概率生成函数

part0 What is it ? 一类人为规定的函数 设 (f(i)) 为第 (i) 项的概率 那么设 (F(x)) 为 (f) 的生成函数 [F(x) = sum_{i geq 0} f(i) * x ^ i ] part1 一些性质 (1: F'(1) = E(x)) 假如第 (i) 项的值为 (i) 则这件事情的期望 (E) 为 [sum_...

C#线程篇---Task(任务)和线程池不得不说的秘密(5)

在上篇最后一个例子之后,我们发现了怎么去使用线程池,调用ThreadPool的QueueUserWorkItem方法来发起一次异步的、计算限制的操作,例子很简单,不是吗?   然而,在今天这篇博客中,我们要知道的是,QueueUserWorkItem这个技术存在许多限制。其中最大的问题是没有一个内建的机制让你知道操作在什么时候完成,也没有一个机制在操作完成...

CFileFind类的详解以及应用实例

CFileFind类在afx.h头文件中声明。功能:执行本地文件的查找,支持通配符。类的成员函数:1、查找操作类: 1 //搜索目录下指定的文件,成功返回非0。第二个参数不必理会2 virtual BOOL FindFile(LPCTSTR pstrName = NULL,DWORD dwUnused = 0); 3 virtual BOOL F...

MySQL行列转换

实际应用中,会遇到需要把表的某些行转换成列,或者把列转换成行的情况。比如一张表在数据库中是这样的:  图1 但是,需要的结果可能是这样:  图2 这个时候就得行列转换了。 1.行转列的几种方法 1.1 case ...  when  ... then ... else ... end select uname,uid, -- 正常查询的字段 sum( ca...

Hrbust 1541集合划分 & Hrbust 2002幂集【dp】

Description 对于从1到N (1 <= N <= 39) 的连续整数集合,能划分成两个子集合,且保证每个集合的数字和是相等的。举个例子,如果N=3,对于{1,2,3}能划分成两个子集合,每个子集合的所有数字和是相等的: {3} 和 {1,2} 这是唯一一种分法(交换集合位置被认为是同一种划分方案,因此不会增加划分方案总数) 如果N=7...

C#基础知识

C#基础 1、面向对象、面向接口、面向方向编程的区别: 面向对象:强调对具有相同行为和属性事物的封装,更注重封装的完整性和功能的完整性 面向接口:定义要实现某类功能要实现的统一规范,而具体实现过程由实现该接口的类型决定 面向方面:主要提供与业务逻辑无关的操作。比如系统中多个地方要使用到的文件上传功能,可以使用面向方向的思想在所有上传文件之前对文件的大小、格...