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

发布于 2024-11-14 09:12:59 字数 319 浏览 3 评论 0原文

在 C# 中,我们无法在重写基类方法时更改访问修饰符。 例如,

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

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

这在 C# 中无效,它将给出编译时错误。

我想知道原因,为什么不允许。是否存在任何技术问题或者是否会导致访问限制方面不一致???

In C#, we can not change access modifier while overriding a method from base class.
e.g.

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.

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???

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

妥活 2024-11-21 09:12:59

更改派生类型中方法的访问修饰符是没有意义的,这就是为什么不允许这样做的原因:

情况 1:使用更严格的访问覆盖

这种情况显然是不允许的,因为以下情况:

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

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

现在我们可以说:

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

情况 2:使用更严格的覆盖进行覆盖限制较少的访问修饰符

有什么意义?隐藏方法就完成了。
显然,如果有人通过基类型调用,他们将无法访问派生类型中定义的新方法,但这与基类型的作者希望的方式一致,因此您没有“权利”更改它。如果您想要从派生类调用派生类的详细信息,在这种情况下,new 方法可以完美地工作。

编辑: 扩展案例 2

我在案例 2 中想说的是,如果您想更改可访问性,您已经有办法更改任何方法(虚拟或非虚拟)的可访问性。

考虑以下代码:

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";
    }
}

使用 new 关键字,您已经有效地为您的 Derived 类创建了一个具有相同名称和签名的新虚拟方法。请注意,允许声明 new 方法 virtual,因此任何从 Derived 派生的类都将被允许重写它。

不允许的是让某人执行以下操作:

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

但这事实与是否能够覆盖 WhoAmI() 无关。不管怎样,这种情况永远不会发生,因为 Base 没有声明 public WhoAmI()

因此,在理论上的 C# 中,Derived.WhoAmI() 可以覆盖 Base.WhoAmI() ,这样做没有实际好处,因为您将永远无法调用虚拟无论如何,来自基类的方法,因此 new 选项已经满足您的要求。

我希望这能让事情变得更清楚。

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

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

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.

EDIT: Expanding case 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.

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. Take note that it is ALLOWED to declare a new method virtual, so any class deriving from Derived will be allowed to override it.

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. Whatever the case this situation can never be because Base does not declare a 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.

I hope this makes it clearer.

听风吹 2024-11-21 09:12:59

好的,我在 Annotated C# 参考中找到了 Eric Lippert 的一个小注释:

重写的虚方法仍然被认为是引入它的类的方法。在某些情况下,重载解析规则更喜欢更多派生类型的成员...重写方法不会“移动”该方法在此层次结构中所属的位置。

因此,这是一个有意的规则,旨在防止“脆弱的基类”问题并提供更好的版本控制,即基类更改时出现的问题更少。

但请注意,它与安全性、类型安全或对象状态无关。

OK, I found a small note from Eric Lippert in the Annotated C# reference:

An overridden virtual method is still considered to be a method of the class that introduced it. 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.

But note that it has nothing to do with security, type-safety or object-state.

南巷近海 2024-11-21 09:12:59

如果将可见性修饰符从限制性更强的修饰符更改为限制性更小的修饰符,则允许类客户端访问指定供内部使用的方法。本质上,您提供了一种改变类状态的方法,但这种方法可能不安全。

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. Essentially you've provided a means to alter class state that may not be safe.

深府石板幽径 2024-11-21 09:12:59

降低可见性是不可能的,因为如果 Base.Member 可见而 Derived.Member 不可见,那么就会破坏整个“Derived is a < OOP 中的“code>Base”概念。然而,增加可见性也许是不允许的,因为语言开发人员认为改变可见性在大多数情况下都是错误的。但是,您始终可以使用 new 关键字通过引入具有相同名称但行为不同的成员来隐藏基类成员。这个新成员属于派生类型的接口,因此当然您仍然可以通过转换为该基类型来访问该基类型的接口。根据您编写子类的方式,您的 new 成员可能会有效地提高基类属性的可见性,但请记住,基类的属性仍然可以直接访问(例如,您的子类的子类可以将 this 转换为 Base 并绕过您的属性)。

这里的问题是如何在子类中同时重写new相同的命名成员(标识符)。这显然是不可能的。至少,我可以通过实验说 public new override string foo(){return "";} 不是这样的语法。但是,您可以通过使用两个子类获得相同的效果:

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."
    }
}

中间子类 (AbstractDerived) 使用 override 并引入一个新的、名称不同的成员,该子类和子类子类可以继续覆盖基类的成员,只要他们认为合适。子子类 (Derived) 使用 new 引入新的 API。由于每个子类化级别只能使用 newoverride 与特定标识符一次,因此您需要两个级别的子类化才能有效地使用这两个级别在同一个标​​识符上。

因此,在某种程度上,您可以在重写方法的同时更改可见性——这只是一种痛苦,而且据我所知,没有任何语法可以仅通过一级继承来完成它。但是,您可能必须使用类似这样的技巧,具体取决于您尝试实现的接口以及您的基类的外观。也就是说,这可能是也可能不是您真正想要做的。但我仍然想知道为什么 C# 一开始就不支持这一点。 IOW,这个“答案”只是OP问题的重新表达和解决方法;-)。

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. 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. 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 (e.g., a subclass of your subclass could cast this to Base and bypass your property).

The question here is how to both override and new the same named member (identifier) in a subclass. 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. 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. The sub-subclass (Derived) uses new to introduce the 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.

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. 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. I.e., 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. IOW, this “answer” is just a re-expression of the OP’s question with a workaround ;-).

茶底世界 2024-11-21 09:12:59

您可以使派生类的访问权限少于基类的访问权限,但不能更多。否则,它将与基础的定义相矛盾,并暴露其组件超出预期。

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.

昇り龍 2024-11-21 09:12:59

原因很明显。对象的安全性和完整性。

在这个特定的示例中,如果外部实体开始修改根据基类保护的对象的属性,会发生什么情况。事情将会变得一团糟。针对所有/任何派生类必须符合的基类编写的客户端代码又如何呢?

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. 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.

噩梦成真你也成魔 2024-11-21 09:12:59

如果它有不同的访问修饰符,你就不能再将它视为相同的方法。有点表明模型设计存在问题。

更好的问题是为什么要更改访问修饰符?

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.

a better question would be why would you want to change the access modifiers?

心在旅行 2024-11-21 09:12:59

重写是一个术语,它使您能够更改或增强基类中方法的行为。重写使您可以控制为现有方法编写新逻辑。

更改基类的方法签名有点像编写一个新方法而不是覆盖现有方法。它与重写方法的目的相矛盾。这也许就是为什么在 C# 中重写方法时无法更改访问修饰符的原因。

Overriding is a term which enables you to change or augment the behavior of methods in a base 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. It contradicts the purpose of overriding a method. So maybe the reason why you cannot change the access modifier while overriding methods in C#.

各自安好 2024-11-21 09:12:59

我的理解是:

继承类时需要记住两件事。

  • 父类公开的API/契约。
  • 父类通过其成员/方法公开的功能。

当尝试决定是否继承一个类时,请独立考虑这两件事。

如果您只想通过类的成员方法公开功能,更好的方法是使用组合而不是继承。这样,您就可以定义类的 API/契约,并将您想要的任何成员、方法保留为公共、私有或受保护。

如果您希望您的类也符合您尝试继承的类的 API,那么就去继承该类。当您继承一个类时,您本质上同意您正在创建的类不会破坏基类保证的 API,因为您的类也是父类类型。保证基类的 API 不被破坏的一部分是不更改访问说明符。

Here is my understanding:

There are two things to keep in mind while inheriting a class.

  • The API / contract exposed by the parent class.
  • The functionality being exposed by the parent class through its members / methods.

When trying to decide whether to inherit a class, consider these two things independently.

If you want only the functionality being exposed by the class's member methods, a better way would be to use composition instead of inheritance. That way, you can define the API / contract of your class and keep whatever members, methods you want as public, private or protected.

If you want that your class should conform to the API of the class you are trying to inherit as well, then go for inheriting the class. When you are inheriting a class, you are essentially agreeing that the class you are creating does not break the API guaranteed by the base class since your class IS ALSO A parent class type. Part of guaranteeing that the base class's API is not broken is to not change the access specifiers.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文