我有一个具体的基类Foo,它包含30个与其子类相关的方法。
现在我遇到了一个仅针对基类的情况,我想创建一个无法继承的方法,这可能吗?
Class Foo
{
/* ... inheritable methods ... */
/* non-inheritable method */
public bool FooSpecificMethod()
{
return true;
}
}
Class Bar : Foo
{
/* Bar specific methods */
}
var bar = new Bar();
bar.FooSpecificMethod(); /* is there any way to get this to throw compiler error */
编辑
我不确定我最初是否清楚。
我理解继承的原则,我理解Liskov替代原则。在这种情况下,只有一个例外处理“未继承”的情况,所以我不想创建一个'uninheritedFoo'子类。
我在问技术上是否有可能创造一种情况 foo.FooSpecificMethod()是一个有效且可公开访问的方法,但是subclassoffoo.FooSpecificMethod()会抛出编译器错误。
基本上我想在一个未密封的类上使用密封方法。
答案 0 :(得分:11)
我会重新考虑这个需要。
如果您正在使用继承,那么您建议“Bar”是“Foo”。如果“Bar”始终是“Foo”,那么处理“Foo”的方法也应该适用于“Bar”。
如果不是这种情况,我会将其作为私有方法重做。公开地,Bar应该永远是Foo。
更进一步 -
如果你能做到这一点,事情会变得非常复杂。您可能遇到以下情况:
Foo myBar = new Bar(); // This is legal
myBar.FooSpecificMethod(); // What should this do?
// It's declared a Foo, but is acutally a Bar
但是,您实际上可以使用反射强制执行此行为。我认为这是一个坏主意,但是FooSpecificMethod()可以检查它的类型,如果它不是typeof(Foo),则抛出异常。这将是非常令人困惑的,并且有一种非常难闻的气味。
编辑以回答问题的编辑:
编译器无法强制执行您的要求。如果你真的想强制编译器检查这个,并防止这种情况,你真的应该考虑让Foo成为一个密封的类。在这种情况下,您可以使用其他扩展方法而不是子类化。
例如,您可能需要考虑使用事件或委托来扩展行为,而不是允许对象成为子类。
尝试做你正在完成的工作基本上是试图阻止继承的主要目标。
答案 1 :(得分:6)
Brian对Liskov Substitution(upmodded)说得对。而里德关于“是一个”(也是上调)的权利;事实上,他们都在告诉你同样的事情。
您的公共方法与您的Foo类用户签订了合同,并说“您可以随时”在Foo上调用这些方法。
子类化Foo意味着你说的是一个子类,例如在可以使用Foo的地方使用Bar总是可以接受的。特别是,它意味着你没有(必然)继承Foo的实现(你可以覆盖它,或者Foo可能是抽象的,并且没有为方法提供特定的实现)。
实现的继承(如果有的话)是一个细节;您真正继承的是公共接口,合同,承诺给用户,Bar可以像Foo一样使用。
实际上,他们甚至可能都不知道他们有一个Bar,而不是Foo:如果我创建了一个FooFactory,并且我写了它的Foo * getAFoo()来返回一个指向Bar的指针,他们可能永远不会知道,并且不必。
当您违反合同时,您将中断对象方向。 (并且Java Collection类通过抛出NotSupported异常,完全破坏了OO - 用户不能再多态地使用所谓的子类。这是糟糕的,糟糕的设计,这给许多Java用户造成了很大的麻烦,不< / em>要模仿的东西。)
如果有一个Foo子类不能使用的公共方法,那么该方法不应该在Foo中,它应该在Foo子类中,而其他子类应该从Foo派生。
现在,这并不意味着Foo中的所有方法都应该在子类上可调用。不,我不是在反驳自己。 非公开方法不属于类的公共接口。
如果你想要一个无法在Bar上调用的Foo方法,和不应该在Foo中公开调用,那么将该方法设为私有或受保护。
答案 2 :(得分:4)
不,这会违反Liskov substitution principle。
实际上,你可以让它在Bar中“抛出NotImplementedException()”,或者从Foo中删除该方法并将其向下移动到它所适用的子类。
答案 3 :(得分:2)
将函数设为私有将阻止子类直接调用它。
如果您正在谈论不希望被重载的虚函数,请在要锁定该函数的位置将该函数标记为已密封。
即使它是一个私有函数,它仍然可以通过反射来调用。
您还可以在接口上声明该函数,并在类上显式实现该接口,这会强制您将其强制转换为接口以使用该函数。
答案 4 :(得分:1)
您可以创建一个私有函数,然后使用反射调用它。可能有点过火了。无论如何,只需将函数放在基类中,同时注释说它只应该从基类调用。甚至可能是那些带有intellisense的好///评论。然后,你可能会遇到一个错误,但是好吧,你总是会遇到错误,你能做的最好就是记录这种情况,试着避免它。
答案 5 :(得分:0)
我最终使用的解决方案是创建一个公共内部继承类。这样它就能够访问基类的私有变量(因为它需要),我不需要公开公开任何这些私有变量或函数(我不想这样做)。
非常感谢你的所有建议,这让我重新考虑了我在这种安排中所需要的东西。
答案 6 :(得分:0)
如果你想要实现你想要做的事情并从私有对象(即你当前继承的那个)公开方法,你应该考虑使用Composition [http://en.wikipedia.org/wiki/Composite_pattern}而不是继承。
答案 7 :(得分:0)
正如其他人所指出的那样,这是不可能的,最重要的是,它不应该是必要的。
当我有以下结构时,我遇到了同样的问题:
- 具有子类属性的父类。
- 子类会暴露不同的属性,但都具有相同的初始化过程,因此我将代码放在父类中。
public class Parent
{
public CHILD1 Child1;
public CHILD2 Child2;
public Parent(params args){
Child1 = new CHILD1(arg1, arg2);
Child2 = new CHILD2(arg1, arg2);
}
//split here
public Parent(object arg1, object arg2) {
PropertyInfo[] list = this.GetType().GetProperties();
foreach (var pi in list) {
pi.SetValue(this, BL.Method(arg1, arg2, this.GetType().Name, pi.Name) // get data
}
}
}
public class CHILD1 : Parent
{
public CHILD1(object arg1, object arg2) : base(arg1, arg2) { }
public object C1Property1 { get; set; }
public object C1Property2 { get; set; }
// ..
}
public class CHILD2 : Parent
{
public CHILD2(object arg1, object arg2) : base(arg1, arg2) { }
public object C2Property1 { get; set; }
public object C2Property2 { get; set; }
// ..
}
现在我可以致电parent.Child1.C1Property1
。但是代码还允许调用以下内容:parent.Child1.Child1.Property1
,这不会有意义并导致异常,因为Child1.Child1
属性始终为null
。
为了解决这个问题,我所要做的就是将父类的代码分成两个类:
答案 8 :(得分:0)
也许您可以简单地在父类上将其创建为虚方法,然后覆盖它,在继承它的子类上抛出 NotImplementedException 或类似的东西.
此外,但前提是您愿意忍受一些意大利面,您可以将方法的签名放在接口中,然后拥有需要这种方法的所有类实施它。缺点是您必须在所有这些孩子中声明相同的方法。
我不确定这是否可能,但也许可以为该接口编写扩展方法?
请记住,这些都违反了 OOP 的封装原则,尽管是轻微的,这让它们变得更加有趣。