C# - 可以隐藏公开继承的方法(例如,对派生类设为私有)

时间:2008-09-19 23:29:04

标签: c# class derived

假设我有公共方法A和B的BaseClass,我通过继承创建DerivedClass。

e.g。

public DerivedClass : BaseClass {}

现在我想在DerivedClass中开发一个使用A和B的方法C.有没有办法可以在DerivedClass中将方法A和B重写为私有,这样只有方法C才会暴露给想要使用我的DerivedClass的人?

10 个答案:

答案 0 :(得分:68)

这不可能,为什么?

在C#中,强制要求你继承公共方法,你必须公开它们。否则他们希望你不要从这个阶段派生出来。

您必须使用has-a关系,而不是使用is-a关系。

语言设计者不会故意允许这样做,以便您更正确地使用继承。

例如,有人可能会意外地将类Car混淆为从类Engine派生以获得它的功能。但引擎是汽车使用的功能。所以你想要使用has-a关系。 Car的用户不希望访问Engine的界面。汽车本身不应该将引擎的方法与它自己的方法混淆。 Nor Car的未来派生。

所以他们不允许它保护你免受糟糕的继承层次结构的影响。

你应该做什么呢?

相反,你应该实现接口。这使您可以使用has-a关系自由使用。

其他语言:

在C ++中,您只需在private,public或protected的基类之前指定一个修饰符。这使得基础的所有成员都公开到指定的访问级别。我觉得你不能在C#中做同样的事情,这似乎很愚蠢。

重组后的代码:

interface I
{
    void C();
}

class BaseClass
{
    public void A() { MessageBox.Show("A"); }
    public void B() { MessageBox.Show("B"); }
}

class Derived : I
{
    public void C()
    {
        b.A();
        b.B();
    }

    private BaseClass b;
}

我理解上述类的名称有点没有实际意义:)

其他建议:

其他人建议将A()和B()公开并抛出异常。但是,这并没有为人们提供友好的课程,而且实际上没有意义。

答案 1 :(得分:24)

例如,当您尝试从List<object>继承时,您想要隐藏直接Add(object _ob)成员:

// the only way to hide
[Obsolete("This is not supported in this class.", true)]
public new void Add(object _ob)
{
    throw NotImplementedException("Don't use!!");
}

这不是最理想的解决方案,但它可以胜任。 Intellisense仍然接受,但在编译时会出现错误:

  

错误CS0619:'TestConsole.TestClass.Add(TestConsole.TestObject)'已废弃:'此类不支持此。'

答案 2 :(得分:6)

这听起来不错。 Liskov不会留下深刻印象。

如果你不希望DerivedClass的使用者能够访问方法DeriveClass.A()和DerivedClass.B()我会建议DerivedClass应该实现一些公共接口IWhateverMethodCIsAbout并且DerivedClass的消费者实际上应该与之交谈IWhateverMethodCIsAbout并且对BaseClass或DerivedClass的实现一无所知。

答案 3 :(得分:5)

您需要的是合成而非继承。

class Plane
{
  public Fly() { .. }
  public string GetPilot() {...}
}

现在,如果你需要一种特殊的平面,比如一个具有PairOfWings = 2但平面可以做的所有平面都可以..你继承平面。通过这种方式,您声明您的派生符合基类的约定,并且可以替换而不会在需要基类的任何地方闪烁。例如LogFlight(Plane)将继续使用BiPlane实例。

但是,如果您只想为要创建的新Bird创建Fly行为,并且不愿意支持完整的基类合约,那么您需要编写。在这种情况下,重构方法的行为以重用到新类型的Flight中。现在在Plane和Bird中创建并保持对此类的引用。 你没有继承,因为Bird不支持完整的基类合同......(例如,它不能提供GetPilot())。

出于同样的原因,在覆盖时不能降低基类方法的可见性。您可以在派生中覆盖并使基本私有方法公开,但反之亦然。例如在这个例子中,如果我派生出一种Plane“BadPlane”,然后覆盖并“隐藏”GetPilot() - 将其设为私有;一个客户端方法LogFlight(Plane p)适用于大多数Planes,但如果LogFlight的实现恰好需要/调用GetPilot(),它将会爆炸为“BadPlane”。 由于预期基类的所有派生都是“可替代的”,因此必须禁止这样做。

答案 4 :(得分:3)

我所知道的唯一方法是使用Has-A关系并仅实现您想要公开的函数。

答案 5 :(得分:3)

@Brian R. Bondy向我指了一篇关于隐藏继承和关键字的有趣文章。

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx

因此,我会建议:

class BaseClass
{
    public void A()
    {
        Console.WriteLine("BaseClass.A");
    }

    public void B()
    {
        Console.WriteLine("BaseClass.B");
    }
}

class DerivedClass : BaseClass
{
    new public void A()
    {
        throw new NotSupportedException();
    }

    new public void B()
    {
        throw new NotSupportedException();
    }

    public void C()
    {
        base.A();
        base.B();
    }
}

这样的代码将抛出 NotSupportedException

    DerivedClass d = new DerivedClass();
    d.A();

答案 6 :(得分:1)

隐藏是一个非常滑的斜坡。 IMO的主要问题是:

  • 这取决于设计时间 实例的声明类型, 意思是你做的事情 然后,BaseClass obj = new SubClass() 调用obj.A(),隐藏被击败。将执行BaseClass.A()。

  • 隐藏很容易模糊 行为(或行为改变) 基本类型。这显然是 当你拥有两者时,不要太担心 等式的两侧,或者如果调用'base.xxx'是你的子成员的一部分。

  • 如果你实际上拥有基础/子类方程的两面,那么你应该能够设计一个比制度化的隐藏/阴影更易于管理的解决方案。

答案 7 :(得分:1)

我想说如果你有一个你想要做的代码库,它不是最好的代码库。它通常是需要某个公共签名的层次结构中的一个类的标志,而从该类派生的另一个类不需要它。

即将推出的编码范例称为“基于继承的组合”。这直接取决于面向对象开发的原则(特别是单一责任原则和开放/封闭原则)。

不幸的是,我们很多开发人员被教导面向对象的方式,我们已经养成了立即考虑继承而不是组合的习惯。我们倾向于拥有更大的类,这些类具有许多不同的职责,仅仅因为它们可能包含在相同的“真实世界”对象中。这可能导致深层次超过5级的类层次结构。

开发人员在处理继承时通常不会考虑的一个令人遗憾的副作用是,继承是您可以引入代码的最强依赖形式之一。您的派生类现在强烈依赖于它继承的类。这可能会使您的代码在长期内更加脆弱,并导致混淆问题,即更改基类中的某个行为会以不明显的方式破坏派生类。

破解代码的一种方法是通过另一个答案中提到的接口。这是一件很聪明的事情,因为你希望类的外部依赖项绑定到抽象,而不是具体/派生类型。这允许您在不更改接口的情况下更改实现,所有这些都不会影响依赖类中的一行代码。

我宁愿维护一个拥有数百/数千甚至更多类的系统,这些系统都是小型且松散耦合的,而不是处理大量使用多态/继承的系统,并且具有更少紧密耦合的类。

对象导向开发中的最佳资源可能就是Robert C. Martin的书,Agile Software Development, Principles, Patterns, and Practices

答案 8 :(得分:0)

如果它们在原始类中被公开定义,则不能将它们覆盖为派生类中的私有。但是,您可以使public方法抛出异常并实现您自己的私有函数。

编辑:Jorge Ferreira是对的。

答案 9 :(得分:0)

虽然这个问题的答案是“不”,但我想指出其他人来到这里(因为OP有点暗示第三方的集会访问)。当其他人引用一个程序集时,Visual Studio应该遵循以下属性,因此它不会显示在intellisense中(隐藏,但仍然可以调用,所以要小心):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]

如果您别无选择,则应该能够在隐藏基本类型方法的方法上使用new,返回=> throw new NotSupportedException();,并将其与上述属性结合使用。

另一个技巧取决于NOT尽可能从基类继承,其中base具有相应的接口(例如IList<T>用于List<T>)。 “显式”实现接口也会在类类型上隐藏intellisense中的那些方法。例如:

public class GoodForNothing: IDisposable
{
    void IDisposable.Dispose() { ... }
}

对于var obj = new GoodForNothing()Dispose()方法将无法在obj上使用obj方法。但是,对于明确键入演员IDisposablepublic class MyList<T> : IList<T> { List<T> _Items = new List<T>(); public T this[int index] => _Items[index]; public int Count => _Items.Count; public void Add(T item) => _Items.Add(item); [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden) /*...etc...*/ } 的任何人都可以使用它。

此外,您还可以包装基类型而不是继承它,然后隐藏一些方法:

GetInstance(bool)