简体   繁体   English

为什么我们不能在覆盖 C# 中的方法时更改访问修饰符?

[英]Why can't we change access modifier while overriding methods in C#?

In C#, we can not change access modifier while overriding a method from base class.在 C# 中,我们无法在从基础 class 覆盖方法时更改访问修饰符。 eg例如

Class Base
{
   **protected** string foo()
   {
       return "Base";
   }
}

Class Derived : Base
{
   **public** override string foo()
   {
       return "Derived";
   }
}

This is not valid in C#, It will give compile time error.这在 C# 中无效,它会给出编译时错误。

I want to know the reason, why it's not allowed.我想知道原因,为什么不允许。 Is there any technical problem or can it lead to something which is not consistent in terms of access restriction???是否存在任何技术问题或是否会导致访问限制不一致???

Changing the access modifier of a method in a derived type is pointless that's why it's not allowed:在派生类型中更改方法的访问修饰符是没有意义的,这就是不允许的原因:

Case 1: Override with a more restrictive access案例 1:使用更严格的访问权限覆盖

This case is obviously not allowed due to the following situation:由于以下情况,这种情况显然是不允许的:

class Base
{
    public virtual void A() {}
}

class Derived: Base
{
    protected override void A()
}

Now we could say:现在我们可以说:

List<Base> list;
list.Add(new Derived());
list[0].A() //Runtime access exception

Case 2: Overriding with a less restrictive access modifier案例 2:使用限制较少的访问修饰符覆盖

What is the point?重点是什么? Hide the method and you are done.隐藏方法,你就完成了。 Obviously if someone calls through the base type they will not have access to the new method defined in the derived type but that is consistent with how the author of the base type wanted things to be so you have no "right" to change that.显然,如果有人通过基类型调用,他们将无法访问派生类型中定义的新方法,但这与基类型的作者想要的东西是一致的,所以你没有“权利”来改变它。 If you want the specifics of the derived class call from the derived class, in which case the new method works perfectly fine.如果您想要从派生的 class 调用派生的 class 的细节,在这种情况下, new方法可以正常工作。

EDIT: Expanding case 2编辑:扩展案例 2

What I am trying to say in case 2, is that you already have the means to change accessibility of any method (virtual or not) if you want to change accessibility.在案例 2 中我想说的是,如果您想更改可访问性,您已经有办法更改任何方法(虚拟或非虚拟)的可访问性。

Consider the following code:考虑以下代码:

public class Base
{
    protected virtual string WhoAmI()
    {
        return "Base";
    }
}

public class Derived : Base
{
    public new virtual string WhoAmI()
    {
        return "Derived";
    }
}

public class AnotherDerived : Derived
{
    public override string WhoAmI()
    {
        return "AnotherDerived";
    }
}

With the new keyword you have effectively created a new virtual method for your Derived class with the same name and signature.使用new关键字,您有效地为Derived class 创建了一个具有相同名称和签名的新虚拟方法。 Take note that it is ALLOWED to declare a new method virtual , so any class deriving from Derived will be allowed to override it.请注意,允许声明一个new方法virtual ,因此任何从Derived的 class 都将被允许覆盖它。

What is not allowed is to have someone do the following:不允许有人做以下事情:

 Base newBaseObject = new Derived();
 newBaseObject.WhoAmI() //WhoAmI is not accessible.

But this fact has nothing to do with being able to override WhoAmI() or not.但是这个事实与是否能够覆盖WhoAmI()无关。 Whatever the case this situation can never be because Base does not declare a public WhoAmI() .无论如何,这种情况永远不会发生,因为Base没有声明public WhoAmI()

So in a theoretical C# where Derived.WhoAmI() could override Base.WhoAmI() there is no practical benefits in doing so because you will never be able to call the virtual method from the base class anyways, so the new option already meets your requirements.因此,在理论上的 C# 中, Derived.WhoAmI()可以覆盖Base.WhoAmI()这样做没有实际好处,因为无论如何您将永远无法从基础 class 调用虚拟方法,因此new选项已经满足您的需求要求。

I hope this makes it clearer.我希望这能让它更清楚。

OK, I found a small note from Eric Lippert in the Annotated C# reference:好的,我在 Annotated C# 参考资料中找到了 Eric Lippert 的一个小注释:

An overridden virtual method is still considered to be a method of the class that introduced it.重写的虚方法仍然被认为是引入它的 class 的方法。 The overload resolution rules in some cases prefer members of more derived types... overriding a method does not "move" where that method belongs in this hierarchy.在某些情况下,重载决议规则更喜欢更多派生类型的成员......覆盖方法不会“移动”该方法在此层次结构中所属的位置。

So this is an intentional rule to prevent the 'brittle base class' problem and provide better versioning, ie less problems when a base class changes.所以这是一个有意的规则,以防止“脆弱的基类”问题并提供更好的版本控制,即当基 class 更改时问题更少。

But note that it has nothing to do with security, type-safety or object-state.但请注意,它与安全性、类型安全性或对象状态无关。

If you change visibility modifiers from a more restrictive modifier to a less restrictive modifier you allow class clients access to methods designated for internal use.如果将可见性修饰符从限制性更强的修饰符更改为限制性较小的修饰符,则允许 class 客户端访问指定为内部使用的方法。 Essentially you've provided a means to alter class state that may not be safe.本质上,您提供了一种方法来更改可能不安全的 class state。

Reducing visibility is impossible because if Base.Member was visible and Derived.Member was not visible, that would break the whole “ Derived is a Base ” concept in OOP.降低可见性是不可能的,因为如果Base.Member可见而Derived.Member不可见,则会破坏 OOP 中的整个“ Derived is a Base ”概念。 However, increasing visibility is disallowed maybe because the language developers think that changing the visibility would be a mistake most of the time.但是,不允许增加可见性可能是因为语言开发人员认为在大多数情况下更改可见性是一个错误。 However, you can always use the new keyword to hide base class members by introducing a member with the same name but a different behavior.但是,您始终可以使用new关键字通过引入具有相同名称但行为不同的成员来隐藏基本 class 成员。 This new member belongs to the derived type's interface, so of course you can still access the base type's interface by casting to that base type.这个新成员属于派生类型的接口,因此您当然仍然可以通过转换为该基类型来访问基类型的接口。 Depending on how you write your subclass, your new member might effectively increase the visibility of the base class's property—but remember that the base class's property can still be accessed directly (eg, a subclass of your subclass could cast this to Base and bypass your property).根据您编写子类的方式,您的new成员可能会有效地增加基类属性的可见性——但请记住,仍然可以直接访问基类的属性(例如,您的子类的子类可以将this强制转换为Base并绕过您的财产)。

The question here is how to both override and new the same named member (identifier) in a subclass.这里的问题是如何子类中overridenew同名成员(标识符)。 That is apparently not possible.这显然是不可能的。 At the very least, I can say through experimentation that public new override string foo(){return "";} is not a syntax for that.至少,我可以通过实验说public new override string foo(){return "";}不是这样的语法。 However, you can get the same effect by using two subclasses:但是,您可以通过使用两个子类来获得相同的效果:

using System;
class Base
{
    protected virtual string foo()
    {
        return "Base";
    }
    public void ExhibitSubclassDependentBehavior()
    {
        Console.WriteLine("Hi, I am {0} and {1}.", GetType(), foo());
    }
}

abstract class AbstractDerived : Base
{
    protected virtual string AbstractFoo()
    {
        return base.foo();
    }
    protected override string foo()
    {
        return AbstractFoo();
    }
}

class Derived : AbstractDerived
{
    protected override string AbstractFoo()
    {
        return "Deprived";
    }
    public new string foo()
    {
        return AbstractFoo();
    }
}

static class Program
{
    public static void Main(string[] args)
    {
        var b = new Base();
        var d = new Derived();
        Base derivedAsBase = d;
        Console.Write(nameof(b) + " -> "); b.ExhibitSubclassDependentBehavior(); // "b -> Hi, I am Base and Base."
        Console.WriteLine(nameof(d) + " -> " + d.foo()); // "d -> Deprived"
        Console.Write(nameof(derivedAsBase) + " -> "); derivedAsBase.ExhibitSubclassDependentBehavior(); // "derivedAsBase -> Hi, I am Derived and Deprived."
    }
}

The intermediate subclass ( AbstractDerived ) uses override and introduces a new, differently-named member that the subclass and sub-subclasses can continue to override the base class's member as they see fit.中间子类 ( AbstractDerived ) 使用override并引入了一个新的、不同名称的成员,子类和子子类可以在他们认为合适的时候继续override基类的成员。 The sub-subclass ( Derived ) uses new to introduce the new API.子类( Derived )使用new引入新的API。 Since you can only use new or override with a particular identifier only once per level of subclassing, you need two levels of subclassing to effectively use both on the same identifier.由于每个子类化级别只能使用new或使用特定标识符override一次,因此您需要两个级别的子类化才能在同一标识符上有效地使用两者。

So, in a way, you can change the visibility while overriding methods—it's just a pain and there's no syntax I know of to accomplish it with just one level of inheritance.因此,在某种程度上,您可以在覆盖方法的同时更改可见性——这只是一种痛苦,而且我不知道仅使用一个级别的 inheritance 来完成它的语法。 However, you might have to use some trick like this depending on what interfaces you're trying to implement and what your base class looks like.但是,您可能必须使用类似这样的技巧,具体取决于您尝试实现的接口以及基本 class 的外观。 Ie, this may or may not be what you actually want to do.即,这可能是也可能不是您真正想要做的。 But I still wonder why C# does not just support this to begin with.但我仍然想知道为什么 C# 一开始就不支持这个。 IOW, this “answer” is just a re-expression of the OP's question with a workaround;-). IOW,这个“答案”只是用解决方法重新表达了OP的问题;-)。

You can make derived class's access less than the base's, but not more.您可以使派生类的访问权限少于基类的访问权限,但不能更多。 Otherwise it would contradict base's definition and expose its components beyond what was intended.否则,它将与 base 的定义相矛盾,并暴露其组件超出预期。

Overriding is a term which enables you to change or augment the behavior of methods in a base class.覆盖是一个术语,它使您能够更改或增加基础 class 中方法的行为。 Overriding gives you the control to write new logic for an existing method.覆盖使您可以控制为现有方法编写新逻辑。

Changing the method signature of a base class is somewhat like writing a new method instead of overriding the existing one.更改基础 class 的方法签名有点像编写新方法而不是覆盖现有方法。 It contradicts the purpose of overriding a method.它与重写方法的目的相矛盾。 So maybe the reason why you cannot change the access modifier while overriding methods in C#.因此,也许是在覆盖 C# 中的方法时无法更改访问修饰符的原因。

Reasons are obvious.原因很明显。 Security and Integrity of the objects.对象的安全性和完整性。

In this particular example, what if external entities start modifying the property of the object which is protected according the base-class.在这个特定的示例中,如果外部实体开始修改 object 的属性,该属性受基类保护。 Things will go haywire.事情将变得混乱。 What about the client-code that is written against the base-class to which all/any derived class must conform to.针对所有/任何派生的 class 必须符合的基类编写的客户端代码呢?

if it had different access modifiers you can't really consider it the same method any more.如果它有不同的访问修饰符,你就不能再认为它是同一种方法了。 kind of suggests a problem with the design of the model.有点暗示 model 的设计存在问题。

a better question would be why would you want to change the access modifiers?一个更好的问题是为什么要更改访问修饰符?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM