防止方法被继承

时间:2009-04-07 23:33:52

标签: c# inheritance-prevention

我有一个具体的基类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()会抛出编译器错误。

基本上我想在一个未密封的类上使用密封方法。

9 个答案:

答案 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

为了解决这个问题,我所要做的就是将父类的代码分成两个类:

  • 持有Child属性并调用子构造函数的人
  • 和一个用构造函数逻辑来填充属性。

答案 8 :(得分:0)

也许您可以简单地在父类上将其创建为虚方法,然后覆盖它,在继承它的子类上抛出 NotImplementedException 或类似的东西.

此外,但前提是您愿意忍受一些意大利面,您可以将方法的签名放在接口中,然后拥有需要这种方法的所有类实施它。缺点是您必须在所有这些孩子中声明相同的方法。

我不确定这是否可能,但也许可以为该接口编写扩展方法?

请记住,这些都违反了 OOP 的封装原则,尽管是轻微的,这让它们变得更加有趣。