考虑以下问题:
你有一个类'A',它可以作为许多其他类似类的基类,例如。一个名为B的课程。
A类在自身中很有用(并且已经在整个地方使用过),因此不是抽象的。
关键是你现在希望强制执行一个新协议,要求所有继承自A(包括A本身)的类实现一个方法'func()'。如果子类忘记实现func(),则应该存在编译器错误。
class A {
A func() { ret new A(...) }
}
class B : A {
A func() { ret new B(...) }
}
class C : A {
// Should give error : no func()
}
问题是否清楚?我不能创建A :: func()抽象,因为我希望能够继续使用A作为具体类。我无法使其成为虚拟,因为这会导致子类回退到超类(而不是给编译器错误)。
接近解决方案的唯一方法是创建一个新的抽象类A *并使所有自定义类型继承自该类,并将A的所有当前用法替换为具有从A继承的新类“DefaultA”的具体类。 *。这看起来很乱。请告诉我还有其他方法可以做到这一点吗?
答案 0 :(得分:10)
如果子类型不覆盖特定方法,则根本无法保持A
具体类型并强制编译器错误。
答案 1 :(得分:2)
好吧,正如你所说,你可以创建一个继承自A的中间抽象类X,并要求派生类继承自X。 X然后声明一个带有签名匹配{{}的受保护抽象方法。 1}}并覆盖A中的func
以调用该方法。这是一个例子:
func
这种方法的主要问题是要求所有派生类现在继承自X而不是A.所以你刚刚将执行问题从一种更改为另一种。
在我看来,更简洁的做法是将class A {
public virtual A func() { ... }
}
abstract class X : A {
protected abstract A funcImpl();
public override A func() { return funcImpl(); }
}
class B : X { /* must now implement funcImpl() to avoid compiler error */ }
class C : X { /* must now implement funcImpl() to avoid compiler error */ }
重构为基类A
,并在那里ABase
抽象。然后密封func
并要求A
和B
继承C
而不是ABase
:
A
您必须将class ABase {
public abstract ABase func(); }
sealed class A : ABase {
public override ABase func() { ... }
}
class B : ABase {
// forced to override func()
public override ABase func() { ... }
}
的所有用法替换为A
- 但Visual Studio中有一些出色的重构工具(以及ReSharper等第三方工具),这使得它比以前更少痛苦是
答案 2 :(得分:1)
如果这是你需要做的,那么是的。
在我看来,您可能希望重新考虑这一要求。我经常实现类似的框架,在子类中强制实现是一个聪明的伎俩。然后我发现,作为这些框架的使用者,我会重复代码或默认为一些无法实现(如空方法)。这些都不是理想的,因为它们会增加杂乱和噪音,并降低可维护性。
此外,如果此模式的应用程序是自行工作(即您的示例具有返回其自身实例的子类),那么您可能希望尝试不同的东西,例如正确的工厂模式。呃,“尝试”我的意思是利用一个控制容器的反转,如Castle Windsor,Ninject,Unity。
答案 3 :(得分:1)
如果A类是抽象的,则无法实例化它。因此,
new A();
和
abstract A func(); // within the A class definition
是矛盾的。
如果你避开抽象,而转向虚拟,你可以得到 运行时 (不是编译时)检查:
// In the A class definition.
protected virtual A func()
{
if (GetType() != typeof(A))
throw // ...
}
答案 4 :(得分:1)
基本上,你要我们“让这个突破,但不要让这个突破。”也许你看到了固有的冲突。
这就是为什么基类只应该用作基类而不能单独使用的原因之一。
答案 5 :(得分:0)
这可能更麻烦,但您可能会强制B,C,D等实现需要func()的接口。然后确保它们总是通过工厂方法构建。所以有点像:
public class B: A, IFunc{
private B(){}
public static B Create(){ return new B(); }
}