柚子Nan--回归原点

Everything can be as easy as you like or as complex as you need.
posts - 232, comments - 984, trackbacks - 17, articles - 29
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

序言:好像在上学的时候,一个老师讲面向对象这门课程,听得云里雾里的,为什么要讲这么多东西?根本都用不到,完全是在创造一种新的规则而已(说到这里,抑制不住地就想多说2句,上学这么多年,每次学到一门新的课程,例如、物理、化学、数学等总是首先给出一堆定理、公式,然后是例题,最后是练习。在学校的时候好学生的定义是什么?成绩好!成绩是怎么好的?各科的成绩都好,为什么呢?他们最大限度的理解了所谓的定理和公式,至于是否真正学到了,而且是要学什么?不得而知。)


废话到此,言归正传:在设计类的时候,用到了关键字Virtualvirtual 关键字用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员。虚拟成员的实现可由派生类中的重写成员更改。调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。

 

默认情况下,方法是非虚拟的。不能重写非虚方法。不能将 virtual 修饰符与以下修饰符一起使用:

static   abstract   override

 

如果方法不是Virtual,编译器就使用声明的引用类型。

 

如果方法是Virtual,编译器就会生成代码,在运行时检查引用指向哪个实例,然后确定这个实例属于哪个类,并调用相应的方法。一般情况下,都应该这样子调用的。

 

那么为什么不把所有的方法都默认为虚拟的呢?有三个方面的考虑 

1、性能:在调用虚拟函数时,需要在运行期间进行检查,确定需要调用哪个重写的方法。对于非虚拟函数,这些信息可以在编译时获得(编译器可以把相关的重写方法与引用声明的类型联系起来),显然,运行时的外部检查会降低性能。这种损失是很小的,虚拟函数在内部执行,这意味着他不再涉及到外部的间接访问级别,但是如果在一个处理器非常密集的大型应用程序中,这种情况每秒发生百万次,因此,如果方法显式的声明为虚拟,就只能按照虚拟方式运行。

2、设计:在设计一个类时,有时其中有一些方法从来不需要重写。这是很常见的情况,特别是那些主要用于在类的内部有其他方法是用的方法,或者方法的执行方式反映了类的内部设计。

3、版本问题:虚拟方法可能会产生与基类的新版本冲突的问题。

Feedback

#1楼    回复  引用    

2004-10-09 10:43 by Ninputer [未注册用户]
对你的考虑我有几条Comment:
1 对虚方法调用的性能影响取决于调用方式而不是方法本身的类型。如果支持用“非虚”的方式调用声明为virtual的函数,那么性能也会和不加virtual函数的调用一样。VB.NET和C++支持非虚调用一个virtual函数,而C#不支持。

2 Java所有的方法都是虚的,开发者也没觉得受到什么限制。事实上我觉得大多数情况,public和protected级别的方法都应该是virtual的。C#/VB/C++这样做可能是为了让开发着自己更清楚哪些方法是应该允许override的。

3 我觉得重写的一定意义在于,父类可以借此调用子类版本的方法。那你觉得父类有了版本变更会在哪里出现冲突呢?

#2楼 [楼主]   回复  引用  查看    

2004-10-09 10:55 by 柚子Nan      
1 、 在调用虚拟函数时,需要在运行期间进行检查,运行时在检查就可能遇到外部检查;而非虚函数,在编译期间就可以确定关联。运行时和编译时的区别应该相当大,“VB.NET和C++支持非虚调用一个virtual函数,而C#不支持”,我想C#是为了杜绝一些现象才这样修改设计的。
2、Java所有的方法都是虚的,没错。也许这就是一个原因为什么Java比C#的程序要慢的一个很小的因素了:)

#3楼 [楼主]   回复  引用  查看    

2004-10-09 10:58 by 柚子Nan      
3、在大多数情况下,如果遇到了基类和继承类的设计问题,我们希望去重写(override)方法.而不是采用new一个方法去做。
具体来说:如果你设计了一个基类A,有相应的属性和方法,然后另外一个类继承了这个类B,在Class B中实现了一个基类中没有的方法Fun1,现在程序可以安全的工作。可是,很多天以后,基类A发现自己需要一个方法Fun1,而A不知道B已经实现了这个方法,即使知道,A与B的Fun1含义可能也会不同,那么A在增加这个方法的时候,会得到一个警告信息,这就是所谓的版本问题,可能会导致出现可怕的调用错函数的问题,这个问题有点微妙!对于这个问题一个比较好的建议是修改函数名称,但是如果你的程序已经在很多客户端实施的话,这个代价有多大呢?

#4楼    回复  引用    

2004-10-09 11:01 by Ninputer [未注册用户]
@柚子Nan
的确有个问题。那这种情况,C#不默认为virtual的特性有什么好处呢。

#5楼    回复  引用    

2004-10-09 16:00 by dali [未注册用户]
如何实现三层以上的virtual调用呢

using System;

class Entry
{
public static void Main()
{
A a = new C();
a.F();
}
}

class A
{
public virtual void F()
{
Console.WriteLine("A");
}
}

class B : A
{
public override void F()
{
Console.WriteLine("B");
}
}


class C : B
{
public new virtual void F()
{
Console.WriteLine("C");
}
}


结果是B,我要求声明时必须用A,结果输出C,如何实现?

#6楼    回复  引用    

2004-10-09 16:07 by aos [未注册用户]
class C : B
{
public override void F()
{
Console.WriteLine("C");
}
}

是也

#7楼 [楼主]   回复  引用  查看    

2004-10-09 16:08 by 柚子Nan      
你为什么在class C : B中使用
public new virtual void F() // new virtual
{
Console.WriteLine("C");
}

你试试这个
public override void F() //override
{
Console.WriteLine("C");
}
具体什么原因,想想......

#8楼    回复  引用  查看    

2004-10-12 22:48 by Ying-Shen      
C#中区分虚函数和非虚函数,这样VM的确可以更精确的为每个方法调用进行优化。

但是这给设计者也带来了很大的责任。他们必须仔细考虑每个属性、方法是否应该设为virtual。而对于庞大的类框架设计时,往往很难预料到实际扩展时的使用方式。
结果只能导致两种情况:
1.全部加上virtual,回到java的老路上。
2.根据设计师的思路去设计virtual,不过这个责任是否太大了?我在使用winform组件的感觉就是很扩功能的扩展就因为某个方法、属性不能override而不能做了。在这一点上我宁愿取扩展性而舍弃性能的(反正winform就那个速度了:) ).

关于override , new关键字,感觉只是用来明确的这种重写、覆盖关系的,在java里面我们无法分辨这个方法是子类定义的还是在override一个父类的方法。


#9楼    回复  引用    

2004-10-13 08:11 by Koffer [未注册用户]
不错啊,设计师就是要考虑这些问题的,什么virtual啊,abstract,interface等等!

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: