C#防止基类方法被派生类中的new修饰符隐藏

Posted

技术标签:

【中文标题】C#防止基类方法被派生类中的new修饰符隐藏【英文标题】:C# prevent base class method from being hidden by new modifier in the derived class 【发布时间】:2013-08-09 09:40:13 【问题描述】:

这是我的情况。在 Java 中,我可以在基类/超类中将方法标记为 final,派生类无法屏蔽具有相同签名的方法。然而,在 C# 中,new 关键字允许继承我的类的人创建具有相同签名的方法。

请参阅下面的示例。我需要保持 orignal.MyClass 公开,所以请不要建议作为答案。这似乎是从 Java 迁移到 C# 时丢失的特性

public class orignal.MyClass
    public void MyMethod()
    
     // Do something
    


class fake.MyClass: orignal.MyClass 
    // How to prevent the following
    public new void MyMethod()
    
     // Do something different
    

编辑:不是重复的。

所有答案似乎都表明,不可能防止方法在派生类中被隐藏/隐藏。在将一些旧的 Java 代码迁移到 C# 时,这一点变得很明显。 Java 中的 final 方法不允许任何人在任何派生类中使用相同的方法签名。虽然在大多数情况下 C# 允许在派生类中使用相同签名的方法是很好的,但如果有必要,防止这种行为也是很好的。

【问题讨论】:

What is the equivalent of Java's final in C#? 的可能副本 ... @Reed 提到,it rarely matters。您的代码仍将调用原始方法。 不是重复的。我不是要一个等价物。我建议这个用例不存在。 【参考方案1】:

//如何防止以下情况

没有办法阻止这种情况。语言允许的。

请注意,在实践中,这并不重要。如果你希望你的基类被使用作为你的类,你的方法仍然会被调用。使用new 仅在使用声明为DerivedClass 的变量中的DerivedClass 时隐藏该方法。

这意味着,如果您的 API 是围绕 MyClass 构建的,那么在将实例传递到您的方法时,仍将始终调用 MyMethod


编辑以响应 cmets:

如果您担心人们通常会继承您的课程,那么您唯一真正的选择就是密封您的课程:

public sealed class MyClass

这将阻止人们完全创建子类。但是,如果您想允许人们从您的类派生,则无法阻止他们将您的方法隐藏在他们的类中。

【讨论】:

我进行了编辑。使用我的 api 的人(比如 Alice)很容易创建一个具有相同名称的类的包装器,然后如果这个包装器被另一个人使用,它将允许 Alice 重新定义 MyMethod( ) 并可能愚弄毫无戒心的最终开发人员 @JAson 并非如此 - 开发人员必须包含其他人的命名空间而不是您的命名空间,或者完全限定类型。无论哪种方式,他们都必须知道自己在做什么。 @JAson 覆盖,而不是隐藏。隐藏方法对基类没有影响。 @JAson 您的情况的前提是有人运行来自不受信任的恶意来源的代码。当你处于那种情况时,你已经输了。适当的发展并不是一开始就把自己放在那个位置上。 @JAson 好吧,不管合理与否,这就是行为,而且不会改变(实施起来会造成太大的破坏性改变)。所以真的没什么好说的了。如果它困扰您,请不要这样做,并接受这样一个事实,即如果他们愿意,其他人可以这样做。【参考方案2】:

您无法阻止公共方法或属性被屏蔽,但您为什么要这样做呢?这需要扩展基类的人故意采取行动(即他们需要输入new),所以他们打算这样做,为什么要阻止他们?

也许您需要稍微改变一下您的模式?如果扩展程序必须使用您的基本方法,那么您可以在其中放入一些关键的东西,从而迫使他们调用它。当然,如果没有正确完成,这很臭,所以如果你使用这种方法,那么将你的方法标记为virtual,然后在文档(或方法头 cmets)中提到必须调用基本方法。这样您就可以避免扩展程序不得不隐藏/屏蔽您的方法(尽管他们仍然可以),但他们仍然可以根据需要扩展它。

【讨论】:

【参考方案3】:

我假设您真的想阻止某人覆盖该方法 - 无法阻止使用 new 隐藏方法,但它不会对基类构成风险,所以应该不是问题。

默认情况下,C# 中的方法是不可覆盖的。因此,您可以通过不标记virtualabstract 来简单地防止某人覆盖基本方法。在 Java 中,方法默认是虚拟的,因此必须使用 final 密封方法以防止被覆盖。

【讨论】:

这个问题似乎很清楚,它是关于新关键字而不是防止覆盖...... 请注意,在 C# 中,您还可以将方法标记为 sealed,其功能实际上与 java 中的 final 相同。您实际上可以密封一个虚拟方法。 IE。有一个由派生类覆盖的虚方法,但将其密封以防止进一步重新定义。 @JAson 你肯定可以在基类中封装一个方法。你绝对可以防止它被覆盖,但是你永远不能用 new 来防止它被遮蔽,就像其他人告诉你的那样。这只是事实。 @Jason - 方法是密封的 @Servy/Stanley:知道了,你是对的。不同的是,您可以使用新修饰符隐藏/隐藏密封方法,这在 Javaland 中是不可能的。猜猜将不得不忍受它。【参考方案4】:

我认为唯一的方法是创建接口,将您的方法定义放入其中,然后让原始类实现接口并显式实现方法:

    interface IAnimal
    
        string diary(string notes, int sleephours);   
    

    class Dogs:IAnimal
            

virtual public string voice()
        
            string v = "Hao Hao"; return v;
         
        string IAnimal.diary(string notes,int sleephours)
        
            return notes + sleep.ToString() + " hours";
        

    

    class Cats:Dogs
    
        public override string voice()
        
            string v = "Miao Miao"; return v;
        
    

您不会通过 Cats 实例使用 diary() 方法。

【讨论】:

以上是关于C#防止基类方法被派生类中的new修饰符隐藏的主要内容,如果未能解决你的问题,请参考以下文章

C#中的新修饰符

override和new的区别

override和new的区别

123

C#基础语法

C# 中基类,虚类,抽象类,密封类,接口的区别