通常会读到对象转换是一种不好的做法,应该避免,例如Why should casting be avoided?问题得到了一些很好的论据答案:
继续我的问题,最近我开始研究最初由Mark Seemann开发的着名开源项目qazxsw poi的源代码,我非常感谢。
该库的主要组件之一是接口AutoFixture,它定义了一种不确定的抽象方法:
ISpecimenBuilder
正如您所看到的,请求参数类型是对象,因此它接受完全不同的类型,接口的不同实现按其运行时类型处理不同的请求,检查它是否是有线处理的内容,否则返回某种无响应表示。
似乎界面的设计并不遵循稀疏使用对象投射的“良好实践”。
我想自己是否有更好的方法来设计这个合同,以一种方式击败所有的铸造,但找不到任何解决方案。
显然,对象参数可以用一些标记界面替换,但它不会为我们节省铸造问题,我也认为可以使用访问者模式的一些变化,如object Create(object request, ISpecimenContext context);
所述,但它似乎不是很可扩展,访问者必须拥有许多不同的方法,因为有很多不同的接口实现能够处理不同类型的请求。
虽然我基本同意反对在这个特定场景中使用铸造作为优秀设计的一部分的观点,但它似乎不仅是最佳选择,而且也是唯一现实的选择。
总而言之,当需要设计模块化和可扩展的架构时,对象投射和非常一般的契约是现实的必然吗?
对于任何类型的应用程序或框架,我认为我不能回答这个问题,但我可以提供一个专门讨论AutoFixture的答案,并提供一些关于其他使用场景的猜测。
如果我今天必须从头开始编写AutoFixture,那肯定会有不同的做法。特别是,我不会像here那样设计日常API。相反,我将围绕仿函数和monad的概念设计数据操作API,如ISpecimenBuilder
。
此设计完全基于泛型,但它确实需要在编译时已知的静态类型构建块(也在文章中描述)。
这与outlined here的作品密切相关。编写基于QuickCheck的测试时,必须为所有自定义类型提供生成器。 Haskell不支持运行时转换值,而是完全依赖于泛型和一些编译时自动化。当然,Haskell的泛型比C#更强大,所以你不一定能将从Haskell获得的知识转移到C#。但是,它的确表明,可以完全编写代码,而无需依赖于运行时强制转换。
但是,AutoFixture支持用户定义的类型,而无需用户编写自定义生成器。它通过.NET Reflection完成此操作。在.NET中,Reflection API是无类型的;生成对象和调用成员的所有方法都将QuickCheck作为输入并返回object
作为输出。
任何基于Reflection的应用程序,库或框架都必须执行一些运行时转换。我不知道怎么解决这个问题。
是否有可能在没有Reflection的情况下编写数据生成器?我没有尝试过以下内容,但也许可以采用一种策略,即直接在IL中为数据生成器编写“代码”,并使用Reflection emit动态编译包含生成器的内存中程序集。
这有点像object
的工作方式,IIRC。我想可以围绕这个概念设计其他类型的通用框架,但我很少看到它在.NET中完成。