【问题标题】:How to hide (remove) a base class's methods in C#? [duplicate]如何在 C# 中隐藏(删除)基类的方法? [复制]
【发布时间】:2010-11-10 16:15:12
【问题描述】:

问题的本质是,给定这样的类层次结构:

class A
{
    protected void MethodToExpose()
    {}

    protected void MethodToHide(object param)
    {}
}

class B : A
{
    new private void MethodToHide(object param)
    {}

    protected void NewMethodInB()
    {}
}

class C : B
{
    public void DoSomething()
    {
        base.MethodToHide("the parameter"); // This still calls A.MethodToHide()
        base.MethodToExpose(); // This calls A.MethodToExpose(), but that's ok
        base.NewMethodInB();
    }
}

如何防止从类“B”继承的任何类看到方法A.MethodToHide()?在 C++ 中,使用 class B : private A 之类的声明很容易做到这一点,但这种语法在 C# 中无效。

对于那些感兴趣的人(或者想知道我真正想要做什么),我们想要做的是为 Rhino.Commons.NHRepository 创建一个包装器,它隐藏了我们不使用的方法不想暴露给我们的开发人员,所以我们可以有一种千篇一律的方式来开发我们的应用程序,新开发人员可以轻松地遵循。所以是的,我相信“Is-A”测试对整个链都有效(WidgetRepository Is-A BaseRepository Is-A NHRepository)。

编辑:为了论证,我应该提到 A 类是我们无法控制的 API 类。否则问题会变得相当容易。

【问题讨论】:

    标签: c# inheritance language-features inheritance-prevention


    【解决方案1】:

    如果您想定制一组功能,我想说您想制作一个包装器而不是继承功能。我也不知道用 C# 做你想做的事。

    想一想,如果您无法控制的某些代码想要这个 NHRepository 实例来做某事……但是您已经从子类中它需要的函数中删除了该功能(您将发送它,因为那是你唯一的NHRepository 实例。)一切都很顺利。这就是为什么我认为没有一些丑陋的黑客是不可能的。

    【讨论】:

    • +1 我敢打赌做一个包装器,而不是继承。与其专注于你想要隐藏的东西,你可以专注于你想要暴露的东西。
    • 我认为最合适的方法是在子类中重写或声明抛出 NotSupportedExceptions 的新方法——这绝对是丑陋的!
    • @Yoooder:我怀疑所有方法都是virtual,所以它们不能被覆盖(并且覆盖可能会扰乱整个类的行为)并试图用new隐藏它们似乎是他已经尝试过的东西(见他的例子。)
    【解决方案2】:

    派生类 B 中的代码不会对所有派生类型隐藏基方法,只会隐藏它自己。您必须在基础中将该方法设置为私有。解决此问题的唯一方法是创建另一个不公开此方法的基类。

    【讨论】:

      【解决方案3】:

      您不能这样做并保留层次结构。如果可能,您应该创建定义理想的接口,然后子类化基类并实现接口。在您的代码中仅引用接口(而不是基类类型)。

      Adapter pattern 专门用于解决当框架的 API 不能完全符合您的需求时如何使用框架的问题。

      【讨论】:

      • +1:我喜欢这个解决方案!只要开发人员坚持使用接口并且不将其显式转换为自定义类,他们就无法访问除接口中指定的内容之外的任何内容。
      • 这主意不错。这与我们已经在做的事情(使用 NHibernate、MVC、依赖注入……)类似,所以我们已经有了存储库的接口(C 类)。这让 C 类的客户对幕后的东西一无所知。剩下要担心的是 C 是如何实现的。在我们的例子中,将有许多“C”级子类(不同对象的存储库)。所以我们的理想可能是让 B 成为 A 的包装器/适配器。不幸的是,A 有很多方法来决定我们是要在 B 中隐藏还是公开。
      【解决方案4】:

      C# 没有类似于 C++ 中受保护或私有继承的概念。

      您最好的选择是聚合该类的一个实例,并公开您感兴趣的一组方法,让您的消费者可以访问。

      尽管在您的情况下我认为这是不可能的,但您可以考虑创建一个接口,该接口仅公开您希望消费者使用的常见功能,以便您的包装器在某些情况下可以替代它的聚合。

      【讨论】:

        【解决方案5】:

        我从不知道你可以在 C++ 中做到这一点,尽管我对 C++ 了解不多。恐怕我同意 Blixt 的观点,即 包装类 可能是我实现这一点的方式。

        简单地覆盖函数并在调用时抛出异常可能会起作用(但我不确定)......

        【讨论】:

        • 这确实有效。我的开发伙伴倾向于这个解决方案,但我并不满意,因为它看起来并不那么干净。
        • C++ 是权威的“你可以做任何你想做的事”的语言,具有很多令人惊讶的灵活性。包括在脚上开枪、上吊、用公共汽车碾过自己的灵活性......
        【解决方案6】:

        如果您使用的是 Visual Studio,则可以使用此属性从智能提示中隐藏方法/属性:

        class A
        {
            protected void MethodToExpose()
            {}
        
            [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]
            protected void MethodToHide(object param)
            {}
        }
        

        它实际上并不会摆脱该功能,但如果他们只是您的内部人员。它可能已经足够接近了。

        【讨论】:

        • 当我第一次发布“A 类”是我们无法控制的 API 类时,我忘了提及。所以修改A类已经结束了。但是,我会玩这个。 [System.ComponentModel.EditorBrowsable(...)] 似乎是一个方便的功能,在某些时候可能会派上用场!
        • 您可以在 B 类中添加属性。
        • 我不确定这是否是一个值得考虑的想法。这将不可避免地引起混乱,这将增加技术债务。
        【解决方案7】:

        据我所知,您无法以您想要的方式隐藏它。我认为您可以阻止它以任何实际方式使用:

        class B : A
        {
            public new void MethodToHide(object param)
            { 
                throw new DontUseThisMethodException();
            }
        
            protected void NewMethodInB()
            {}
        }
        

        虽然不是最好的事情,所以你可能想以其他方式解决它......

        【讨论】:

        • 我的同事决定使用这种方法。不过,在编译时而不是在运行时隐藏它会更好。
        • 抛出新的 notimplementedException 或某种类型是不可以的。它违反了接口隔离原则
        【解决方案8】:

        实际上,如果您将 B 的方法定义为可从 C 访问,那么您可以从 C 隐藏 A 的方法。

        您的代码的唯一问题是您在隐藏声明中使用了“私有”...如果您使用受保护或公共,则不会有任何问题,它会按您的预期运行。我一直在用字段来做这件事。

        【讨论】:

        • 我想我明白你想说什么,但我真的不希望 C 类看到 MethodToHide(param) 的任何版本,无论它是继承自 B 还是 A。同事的解决方案不过,与您的建议一致。他覆盖了 B 中的 MethodToHide(param),但有额外的步骤使 B.MethodToHide(param) 抛出 NotImplementedException。类似于 Fredrick 的解决方案。
        • @OgrePsalm33:使用该名称和不兼容的签名创建一些其他成员 - 可能是一个类型为 enum 的只写属性,称为 DontEvenThinkAboutIt,其唯一定义的成员是 @987654323 @.
        【解决方案9】:

        过时了

        在 B 类中,重写 MethodToHide 并添加 Obsolete 属性

        [Obsolete("Reason", true)] // true will cause a compile-time error
        

        设置 EditorBrowsable

        (如前所述)

        在 B 类中,重写 MethodToHide 并添加 EditorBrowsable 属性

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

        抛出异常

        (如前所述)

        在 B 类中,重写 MethodToHide 并抛出异常。

        创建包装器

        我认为迈克尔·梅多斯是对的。使用Adapter pattern。这种模式还允许在单元测试时更轻松地模拟代码。

        class B: IInterface
        {    
            protected void MethodToExpose()
            {
                A a = new A();
                a.MethodToExpose();
            }
        
            protected void NewMethodInB()
            {
            }
        }
        

        【讨论】:

        • 有趣的方法!我很惊讶没有人提到过时的属性。可能不完全是该属性的初衷,但我认为合适。
        • 添加一个完全不同类型的新成员怎么样?也许是一个空的公共静态类?我不确定所有 .NET 语言如何处理阴影,但我认为新类将隐藏所有具有相同名称的基类成员。
        • 添加Obsolete 属性听起来是个好主意,但不幸的是它会抛出a compiler warning。错误属性也将无效。
        • @DavidSherret 如果我是正确的,这应该删除警告:#pragma warning disable 0809
        • 比接受的答案更好
        【解决方案10】:

        在我看来,如果您只是想隐藏而不是真正覆盖和外部化新功能,那么最好的方法就是添加一个 EditorBrowsableState 属性。 它隐藏了 Visual Studio 编辑器中的方法。谁会尝试使用它,最终会出现编译时错误。

        只需将此代码添加到您的方法之上:

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

        例如

        public class User
        {        
            public void DoSomething()
            {
               Something...
            }
        }
        
        public class Manager
        {
            [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)]
            public override void DoSomething()
            { }
        }
        
        void main()
        {
           User user = new User();
           user.DoSomething();
        
           Manager manager = new Manager();
           manager.DoSomething(); // --------- This row will throw a design time error
        }
        

        祝你好运:)

        【讨论】:

        • [System.ComponentModel.EditorBrowsable(EditorBrowsableState.Never)] 不会在 LINQPad 中引发设计时错误。更重要的是:它由 Intellisense 显示的。没有检查过 Visual Studio,如果代码是为你自己以外的任何人准备的,这显然不是要走的路。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-17
        • 2013-02-18
        • 1970-01-01
        • 2011-09-14
        • 1970-01-01
        • 2011-04-18
        相关资源
        最近更新 更多