"C#类中虚方法相互调用的潜在重载错误"相关思考
2005-02-25 15:07
453 查看
原文见/article/4589028.html
birdshome在原文中引用了我给他的一个测试代码,但那个测试代码原意只是为了说明在Derived类的Foo中调用base.Foo时,因为多态的关系,Derived的Bar会被调用,而Base类本身的接口设计是有问题的。
“其实这确实是面向对象中的一个设计矛盾,等于平白的无故的把一些语义规则强加给了程序员,而且还是相当隐讳的。”
在OOD和OOP中,最重要的是Thinking in Object,语言所提供的面向对象的功能,更多的是表达出来的一种语义,表达的是对象的一种特性。C#中的virtual,不仅仅表示某一个方法是一个虚函数,更重要的是,它为对象的方法添加了多态的能力,意味着类的设计者希望这个类能够被派生,并且允许在派生类中对此方法进行重载。而此,语义的规则,反映的是程序员所设计的对象的特征,而不是实现相关的。OOD的第一步是抽象,如果没有抽象出类所代表的对象的特性,那么在实现过程中,就会因为对语言功能的错误使用而产生具有错误语义的抽象。
回过头来看Base类的接口设计,原来的代码如下:
public class Base
从面向对象语义和对象接口设计的角度来看,Base类的接口设计是有很多问题的。
首先,Foo做为一个public的接口,却调用了另一个public Bar方法,从接口设计上来说,是功能重叠,不满足面向对象设计中的最小化接口要求。
其次,Foo是virtual方法,这意味着Foo的实现会被派生类重载,而在Foo的实现中,调用了方法Bar。派生类是没有责任在Foo的重载中调用Bar方法的,这意味着基类的逻辑不具有传递性。
第三,Foo本身是virtual方法,而其调用的Bar也是virtual方法,这导致派生类具有过强的重载能力,这种能力常常会造成逻辑上的冲突。
因此,Base类的接口需要重新设计,可以参考Template Method模式,重构如下:
public class Base
public void Foo()
this.DoFoo();
this.DoBar();
}
public void Bar()
this.DoBar;
}
protected virtual void DoFoo()
}
protected virtual void DoBar()
}
};
当然,真正适合的接口设计要参照应用的场景,是不能简单的通过一个示例代码来说明的。
而原文的问题,是因未做if (obj != null)的测试而引起的,因为Bar本身也是一个public方法,能够被其他方法调用。从Derived类的接口设计上来看,没有Foo一定在Bar调用前的语义,而此必要的判断是必需的。
birdshome在原文中引用了我给他的一个测试代码,但那个测试代码原意只是为了说明在Derived类的Foo中调用base.Foo时,因为多态的关系,Derived的Bar会被调用,而Base类本身的接口设计是有问题的。
“其实这确实是面向对象中的一个设计矛盾,等于平白的无故的把一些语义规则强加给了程序员,而且还是相当隐讳的。”
在OOD和OOP中,最重要的是Thinking in Object,语言所提供的面向对象的功能,更多的是表达出来的一种语义,表达的是对象的一种特性。C#中的virtual,不仅仅表示某一个方法是一个虚函数,更重要的是,它为对象的方法添加了多态的能力,意味着类的设计者希望这个类能够被派生,并且允许在派生类中对此方法进行重载。而此,语义的规则,反映的是程序员所设计的对象的特征,而不是实现相关的。OOD的第一步是抽象,如果没有抽象出类所代表的对象的特性,那么在实现过程中,就会因为对语言功能的错误使用而产生具有错误语义的抽象。
回过头来看Base类的接口设计,原来的代码如下:
public class Base
从面向对象语义和对象接口设计的角度来看,Base类的接口设计是有很多问题的。
首先,Foo做为一个public的接口,却调用了另一个public Bar方法,从接口设计上来说,是功能重叠,不满足面向对象设计中的最小化接口要求。
其次,Foo是virtual方法,这意味着Foo的实现会被派生类重载,而在Foo的实现中,调用了方法Bar。派生类是没有责任在Foo的重载中调用Bar方法的,这意味着基类的逻辑不具有传递性。
第三,Foo本身是virtual方法,而其调用的Bar也是virtual方法,这导致派生类具有过强的重载能力,这种能力常常会造成逻辑上的冲突。
因此,Base类的接口需要重新设计,可以参考Template Method模式,重构如下:
public class Base
public void Foo()
this.DoFoo();
this.DoBar();
}
public void Bar()
this.DoBar;
}
protected virtual void DoFoo()
}
protected virtual void DoBar()
}
};
当然,真正适合的接口设计要参照应用的场景,是不能简单的通过一个示例代码来说明的。
而原文的问题,是因未做if (obj != null)的测试而引起的,因为Bar本身也是一个public方法,能够被其他方法调用。从Derived类的接口设计上来看,没有Foo一定在Bar调用前的语义,而此必要的判断是必需的。
相关文章推荐
- C#类中虚方法相互调用的潜在重载错误
- JS和C#方法相互调用
- (摘)C++和C#相互调用COM组件的方法简介
- JS和C#方法相互调用
- C#中构造方法重载的相互调用
- js与C#之间相互调用的一些方法
- 第十五讲:重载(实例补充之构造函数及普通方法,各自相互调用)
- C++和C#相互调用COM组件的方法简介
- WinForm中嵌入WebBrowser,并且支持C#和JS方法的相互调用
- js与C#之间相互调用的一些方法
- C#错误:不能以方法的方式使用不可调用的
- C#中调用DLL时未能加载文件或程序集错误的处理方法(详解)
- (摘)C++和C#相互调用COM组件的方法简介
- C# 调用IP库(QQWry.Dat)查询IP位置及自动升级IP库方法(附IP库下载地址及相关dll下载)
- 【C#基础】方法及其调用、构造方法、out与ref参数及其返回值、方法重载、静态方法等简单介绍
- C#调用C++的dll 常见错误解决方法
- 【C#基础】方法及其调用、构造方法、out与ref参数及其返回值、方法重载、静态方法等简单介绍
- Java编程中在子类重载方法中调用父类中该方法时,该方法包含递归时出现的调用错误
- 关于C#操作PPT时遇到的“对COM组件的调用返回了错误HRESULT E_FAIL”错误的解决方法
- Struts2.xml文件中的常量、全局错误的配置及相关动态方法的调用