我一直在考虑使用扩展方法来替代抽象基类。扩展方法可以提供默认功能,并且可以通过在派生类中放置具有相同签名的方法来“重写”它们。
是否存在任何不应该这样做的理由?
此外,如果我有两个具有相同签名的扩展方法,哪一个会被使用?是否有一种确定优先级的方法?
我一直在考虑使用扩展方法来替代抽象基类。扩展方法可以提供默认功能,并且可以通过在派生类中放置具有相同签名的方法来“重写”它们。
是否存在任何不应该这样做的理由?
此外,如果我有两个具有相同签名的扩展方法,哪一个会被使用?是否有一种确定优先级的方法?
一般来说,不应通过扩展方法提供“基础”功能。它们只应用于“扩展”类功能。如果您可以访问基类代码,并且您正在尝试实现的功能在逻辑上属于继承层次结构的一部分,则应将其放在抽象类中。
我的观点是,仅仅因为你可以并不意味着你应该这样做。通常最好坚持传统的面向对象编程(OOP),当平凡的OO编程不能提供合理的解决方案时再使用新的语言特性。
这绝对是一个不好的想法。扩展方法是静态绑定的,这意味着除非您在编译时类型为子类型的对象上调用覆盖,否则您仍将继续调用扩展方法。再见多态性。此页面对扩展方法的危险进行了很好的讨论。
它们是语义上不同的操作。例如,多态性可能无法像使用抽象基类那样工作。
通常情况下,使用任何语言工具都要按照其设计目的使用。扩展方法并不是继承的替代品,而是一种使用(通常是已经可见的)接口来扩展类功能的技术。
有很多原因你不应该这样做。首先,你无法保证如何调用你的扩展:
MyExtensions.AMethod(myObj)
或者
myObj.AMethod()
如果您不需要独特的派生类,那么您可能想考虑放弃多态性。
以下是来自 MSDN 文章 的一些通用指南:
一旦在类中定义了一个方法,任何共享该方法名称的该类的扩展方法将不再执行。
扩展方法按命名空间加载。为了允许其他扩展方法的用户,您应该避免将扩展方法放在与被扩展的类相同的命名空间中。
如果各个依赖程序集正在使用同一库,并且每个程序集都需要针对库中的类进行自定义函数,则可以在该程序集内的命名空间中声明特定于依赖程序集的扩展。不依赖于该程序集的其他程序集将没有该扩展。
虽然这不是多态性,但您可能不想使用多态性,因为它要求所有派生类实现其重写。
换句话说,如果您有具有特定变体逻辑的派生类,请使用多态性。 如果您只是需要在特定情况下使用自定义功能,否则可能会令人困惑或繁琐,那么扩展方法并不是一个坏主意。
另外,可以参考JoshRivers的答案,展示如何使用一个类内部的命名空间覆盖扩展方法。首先,检查[new]作为方法修饰符可能是个不错的选择 - 它应该提供相同的性能和方法分离,但会减少很多麻烦。
如果在那之后你仍在考虑同样的技巧,下面是要考虑的主要选项 - 没有涉及意识形态,只是列举需要注意的方面 :-)
首先,您可能需要将所有使用限制为模板化函数参数,以保证确定性函数选择,因为这个技巧将创建与CLR在函数调用边界上保证确定性绑定优先级完全相反的情况。
如果您仍然要使用抽象基类,并且您拥有所有代码,那么仅将一个基本方法“转换”为扩展将一无所获,而且还会使您失去一个共同接口,这个接口仍将存在于其他所有内容中,包括vtable的成本。
如果你的目标是将抽象类转换为具体类,出于性能原因消除所有虚方法,并在模板中仅使用具体基类和所有派生类(甚至转换为结构体),那么这个技巧可以帮助你提高性能。你只需要意识到,一旦进行了重构,就无法回到基于接口的使用方式。你还需要测试非模板函数中带有基类签名的调用顺序,并且如果你有多个dll,则需要特别小心。尝试使用[new]运算符进行相同的操作,看看是否效果更好。
此外,即使它们具有完全相同的功能,你也必须为每个派生类编写单独的方法实现,因此如果你有很多派生类,你将面临大量的代码重复。即使你使用[new],这部分也会影响你,除非你回到虚方法并在继承中引入另一层 - 这当然会取消所有性能收益。