为什么我不能像这样访问 C# 受保护成员?

发布于 2024-07-13 17:57:58 字数 707 浏览 7 评论 0原文

此代码:

abstract class C
{
    protected abstract void F(D d);
}

class D : C
{
    protected override void F(D d) { }

    void G(C c)
    {
        c.F(this);
    }
}

生成此错误:

无法通过“C”类型的限定符访问受保护成员“CF(D)”; 限定符必须是“D”类型(或派生自它)

他们到底在想什么? (改变这条规则会破坏某些东西吗?)除了公开 F 之外,还有其他方法可以解决这个问题吗?


编辑:我现在明白了为什么会这样(谢谢 Greg)但我仍然对理性感到有点困惑; 给出:

class E : C
{
    protected override void F(D d) { }
}  

为什么 D 不应该能够调用 EF?


错误消息已被编辑,因此我可能在那里输入了拼写错误。

This code:

abstract class C
{
    protected abstract void F(D d);
}

class D : C
{
    protected override void F(D d) { }

    void G(C c)
    {
        c.F(this);
    }
}

Generates this error:

Cannot access protected member 'C.F(D)' via a qualifier of type 'C'; the qualifier must be of type 'D' (or derived from it)

What in the world were they thinking? (Would altering that rule break something?) And is there a way around that aside from making F public?


Edit: I now get the reason for why this is (Thanks Greg) but I'm still a bit perplexed as to the rational; given:

class E : C
{
    protected override void F(D d) { }
}  

Why shouldn't D be able to be able to call E.F?


The error message is edited so I might have put a typo in there.

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

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

发布评论

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

评论(7

不再让梦枯萎 2024-07-20 17:57:58

这不起作用的原因是 C# 不允许跨层次调用受保护的方法。 假设有一个类 E 也派生自 C

  C
 / \
D   E

那么您尝试调用该方法的引用实际上可能是 E< 类型的实例/code> 因此该方法可以在运行时解析为 EF。 这在 C# 中是不允许的,因为 D 无法调用 E 的受保护方法,因为 E 位于层次结构的另一个分支中,即

var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D

这使得这是有道理的,因为关键字 protected 表示成员“可在以下范围内访问:它的类和派生类实例”并且 EF 不是 D 的成员。

The reason this doesn't work is because C# doesn't allow cross-hierarchy calling of protected methods. Say there was a class E that also derived from C:

  C
 / \
D   E

Then the reference you're trying to call the method on could actually be an instance of type E and thus the method could resolve at runtime to E.F. This is not permitted in C# as D cannot call E's protected methods, because E is in another branch of the hierarchy, i.e.

var d = new D();
var e = new E();
d.G(e); // oops, now this will call E.F which isn't allowed from D

This makes sense because the keyword protected means the member "is accessible within its class and by derived class instances" and E.F is not a member of D.

眼泪也成诗 2024-07-20 17:57:58

“protected”关键字意味着只有一个类型和从该类型派生的类型才能访问该成员。 D 与 C 没有关系,因此无法访问该成员。

如果您希望能够访问该成员,您有几个选择:

  • 将其公开、
  • 将其设为内部。 这将允许任何类型访问同一程序集(或您添加朋友的其他程序集)内的成员。
  • 从 C 派生 D

编辑

这种情况在 C# 规范的第 3.5.3 节中提到。

不允许这样做的原因是它允许跨层次结构调用。 想象一下,除了 D 之外,还有另一个名为 E 的 C 基类。如果您的代码可以编译,它将允许 D 访问成员 EF 这种情况在 C# 中是不允许的(我相信 CLR,但我不是 100% 知道)。

EDIT2 为什么这是不好的

警告,这是我的观点

现在允许这样做的原因是它使得很难推理一个类的行为。 访问修饰符的目标是让开发人员准确控制谁可以访问特定方法。 想象一下下面的类

sealed class MyClass : C {
  override F(D d) { ... } 
}

考虑如果 F 是一个对时间要求严格的函数会发生什么。 通过当前的行为,我可以推断出我的类的正确性。 毕竟只有两种情况会调用 MyClass.F。

  1. 在 C 中调用它的地方
  2. 在 MyClass 中显式调用它的地方,

我可以检查这些调用并得出有关 MyClass 如何运行的合理结论。

现在,如果 C# 确实允许跨层次结构的受保护访问,我无法做出这样的保证。 完全不同的程序集中的任何人都可以从 C 来并派生。然后他们可以随意调用 MyClass.F。 这使得完全不可能推断我的课程的正确性。

The "protected" keyword means that only a type and types that derive from that type can access the member. D has no relationship to C therefore cannot access the member.

You have a couple of options if you want to be able to access that member

  • Make it public
  • Make it internal. This will allow any types to access the member within the same assembly (or other assemblies should you add friend's)
  • Derive D from C

EDIT

This scenario is called out in section 3.5.3 of the C# spec.

The reason this is not allowed is because it would allow for cross hierarchy calls. Imagine that in addition to D, there was another base class of C called E. If your code could compile it would allow D to access the member E.F. This type of scenario is not allowed in C# (and I believe the CLR but I don't 100% know).

EDIT2 Why this is bad

Caveat, this is my opinion

The reason this is now allowed is it makes it very difficult to reason about the behavior of a class. The goal of access modifiers is to give the developer control over exactly who can access specific methods. Imagine the following class

sealed class MyClass : C {
  override F(D d) { ... } 
}

Consider what happens if F is a somewhat time critical function. With the current behavior I can reason about the correctness of my class. After all there are only two cases where MyClass.F will be called.

  1. Where it's invoked in C
  2. Where I explicitly invoke it in MyClass

I can examine these calls and come to a reasonable conclusion about how MyClass functions.

Now, if C# does allow cross hierarchy protected access I can make no such guarantee. Anyone in a completely different assembly can come by and derive from C. Then they can call MyClass.F at will. This makes it completely impossible to reason about the correctness of my class.

坏尐絯 2024-07-20 17:57:58

即使 D 继承自 C,D 也无法访问 C 的受保护成员。 D 可以访问 D 的受保护(和私有!)成员,因此如果您将 D 的另一个实例而不是 C 放在那里,一切都会正常。 但正如 Greg 所说,C 确实可能是完全不同的东西,并且因为编译器不知道 C 到底是什么,所以它必须阻止 D 访问 D 实际上可能无法访问的东西。

一系列帖子从 C# 编译器的角度解释了这一点:

Even though D is inherits from C, D cannot access C's protected members. D can access D's protected (and private!) members, so if you put another instance of D in there instead of C everything would work. But as Greg stated, C could really be something completely different, and because the compiler doesn't know what C really is, it has to prevent D from accessing something D may not actually be able to access.

A series of posts explaining this from the C# compiler's perspective:

青萝楚歌 2024-07-20 17:57:58

可以通过使用静态受保护方法来绕过此限制:

abstract class C
{
    protected abstract void F (D d);

    // Allows calling F cross-hierarchy for any class derived from C
    protected static void F (C c, D d)
    {
        c.F(d);
    }
}

class D : C
{
    protected override void F (D d) { }

    void G (C c)
    {
        // c.F(this);
        F(c, this);
    }
}

从安全角度来看这并不完美(任何人都可以从C派生),但如果您关心的是隐藏方法F 从类 C 的公共接口来看,这个技巧可能很有用。

This limitation can be bypassed by using a static protected method:

abstract class C
{
    protected abstract void F (D d);

    // Allows calling F cross-hierarchy for any class derived from C
    protected static void F (C c, D d)
    {
        c.F(d);
    }
}

class D : C
{
    protected override void F (D d) { }

    void G (C c)
    {
        // c.F(this);
        F(c, this);
    }
}

This isn't perfect from security point of view (anyone can derive from C), but if all you care about is hiding method F from the public interface of the class C, this trick may be useful.

简单气质女生网名 2024-07-20 17:57:58

简而言之:访问实例的受保护成员将被视为公共访问,即使您尝试从派生类中执行此操作。 因此,它被否认。


这里和那里有很多答案,但没有一个让我清楚“为什么我无法从孩子访问父类的受保护成员”。 以上是我在阅读这些令人困惑的答案后再次查看我的代码后所理解的。

示例:

class Parent
{
    protected int foo = 0;
}

// Child extends from Parent
class Child : Parent
{
    public void SomeThing(Parent p)
    {
        // Here we're trying to access an instance's protected member.
        // So doing this...
        var foo = p.foo;
    }
}

// (this class has nothing to do with the previous ones)
class SomeoneElse
{
    public void SomeThing(Parent p)
    {
        // ...is the same as doing this (i.e. public access).
        var foo = p.foo++;
    }
}

您认为您可以访问 p.foo 因为您位于子类中,但您是从实例访问它,这就像公共访问,所以它是否认。

您可以从类内部访问受保护的成员,而不是从实例(是的,我们知道这一点):

class Child : Parent
{
    public void SomeThing()
    {
        // I'm allowed to modify parent's protected foo because I'm
        // doing so from within the class.
        foo++;
    }
}

最后,为了完整起见,您实际上可以访问实例的>protected 甚至 private 成员(仅当您在同一个类中这样做时):

class Parent
{
    protected int foo = 0;

    private int bar = 0;

    public void SomeThing(Parent p)
    {
        // I'm allowed to access an instance's protected and private
        // members because I'm within Parent accessing a Parent instance
        var foo = p.foo;
        p.bar = 3;
    }
}

Simply put: accessing an instance's protected member is taken as a public access, even if you try to do so from within a derived class. Hence, it's denied.


There are many answers here and there, but none of them made it clear to me "why I can't access the protected members of a parent class from a child". That above is what I understood after looking at my code again after reading these confusing answers.

Example:

class Parent
{
    protected int foo = 0;
}

// Child extends from Parent
class Child : Parent
{
    public void SomeThing(Parent p)
    {
        // Here we're trying to access an instance's protected member.
        // So doing this...
        var foo = p.foo;
    }
}

// (this class has nothing to do with the previous ones)
class SomeoneElse
{
    public void SomeThing(Parent p)
    {
        // ...is the same as doing this (i.e. public access).
        var foo = p.foo++;
    }
}

You'd think you'd have access to p.foo because you're inside a child class, but you're accessing it from a instance, and that's like a public access, so it's denied.

You're allowed to access protected members from within the class, not from an instance (yes, we know this):

class Child : Parent
{
    public void SomeThing()
    {
        // I'm allowed to modify parent's protected foo because I'm
        // doing so from within the class.
        foo++;
    }
}

Finally, for the sake of completeness, you are actually able to access an instance's protected and even private members only if you're doing so within the same class:

class Parent
{
    protected int foo = 0;

    private int bar = 0;

    public void SomeThing(Parent p)
    {
        // I'm allowed to access an instance's protected and private
        // members because I'm within Parent accessing a Parent instance
        var foo = p.foo;
        p.bar = 3;
    }
}
⒈起吃苦の倖褔 2024-07-20 17:57:58

为了理解为什么这种行为有意义,让我们考虑一下为什么在面向对象的编程语言中需要访问修饰符。 我们需要它们来限制特定类成员的使用范围。 这反过来又简化了对用法的搜索。

总结一下:

  • 要查找 public 成员的所有用法,需要搜索整个项目(对于独立开发人员使用的库,这还不够)
  • 才能找到protected成员的所有用法都需要搜索容器类及其所有子类
  • 才能找到需要搜索的private成员的所有用法容器类

因此,如果编译器允许以所描述的方式从超类调用受保护的方法,我们最终可能会跨层次调用受保护的方法,如 这个答案。 在这种情况下,我们必须搜索定义该成员的最父类的所有子类。 这会扩大范围。

附言。 Java 中也实现了相同的行为。

To understand why this kind of behavior makes sense let's consider why we need access modifiers at all in object oriented programming languages. We need them to limit a scope where a particular class member can be used. And that in turn simplifies searching for the usages.

To summarize:

  • to find all usages of public member one needs to search through entire project (and this is not enough in case of library that is used by independent developers)
  • to find all usages of protected member one needs to search through the container class and all its subclasses
  • to find all usages of private member one needs to search through the container class.

So if the compiler allowed to call protected method from superclass in the described way we could end up with cross-hierarchy calling of protected methods as described in this answer. And in such situation one had to search through all the children of the most parent class that defines the member. And that would increase the scope.

PS. The same behavior is implemented in Java.

鲜肉鲜肉永远不皱 2024-07-20 17:57:58

对的,这是可能的。 我们很可能很快就会有这样的例子。

为此,您必须执行以下操作:

  1. 继承默认表单 (EditAppointmentDialog) 并进行自定义(您甚至可以使用 winforms 设计器来实现)。

公共部分类 CustomAppointmentEditDialog : EditAppointmentDialog
{
私有 RadComboBox cmbShowTimeAs = null;

    public CustomAppointmentEditDialog() 
    { 
        InitializeComponent(); 

        this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox; 
    } 

    private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args) 
    { 
        this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ? 
            (int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative; 
    } 
} 

在上面的代码中,我添加了一个附加复选框,并将约会的状态(显示时间为)设置为“暂定”(如果未选中)或“忙碌”(如果选中)。 访问组合框的奇怪方式是因为它当前是私有的。 这将在即将发布的 2009 年第一季度版本中进行更改。

  1. 订阅 RadScheduler 的 AppointmentEditDialogShowing 事件,并将默认表单替换为您自定义的表单:

private IEditAppointmentDialog AppointmentEditDialog = null;

    protected override void OnLoad(EventArgs e) 
    { 
        base.OnLoad(e); 

        this.radScheduler1.AppointmentEditDialogShowing += new EventHandler<AppointmentEditDialogShowingEventArgs>(radScheduler1_AppointmentEditDialogShowing); 
    } 

    void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e) 
    { 
        if (this.appointmentEditDialog == null) 
        { 
            this.appointmentEditDialog = new CustomAppointmentEditDialog(); 
        } 
        e.AppointmentEditDialog = this.appointmentEditDialog; 
    } 

我希望这有帮助。 如果您还有其他问题,请随时给我回信。

Yes, it is possible. We will most probably have such an example very soon.

To do that you must do the following:

  1. Inherit the default form (EditAppointmentDialog) and do your customization (you can even use the winforms designer for that).

public partial class CustomAppointmentEditDialog : EditAppointmentDialog
{
private RadComboBox cmbShowTimeAs = null;

    public CustomAppointmentEditDialog() 
    { 
        InitializeComponent(); 

        this.cmbShowTimeAs = this.Controls["cmbShowTimeAs"] as RadComboBox; 
    } 

    private void chkConfirmed_ToggleStateChanged(object sender, StateChangedEventArgs args) 
    { 
        this.cmbShowTimeAs.SelectedValue = (args.ToggleState == ToggleState.On) ? 
            (int)AppointmentStatus.Busy : (int)AppointmentStatus.Tentative; 
    } 
} 

In the above code I have added an additional check box and set the status (show time as) of the appointment to Tentative if it is unchecked or to Busy if it is checked. The strange way of accessing the combo box is because it is private currently. This will be changed for the upcoming Q1 2009 release.

  1. Subscribe to AppointmentEditDialogShowing event of RadScheduler and substitute the default form with your customized one:

private IEditAppointmentDialog appointmentEditDialog = null;

    protected override void OnLoad(EventArgs e) 
    { 
        base.OnLoad(e); 

        this.radScheduler1.AppointmentEditDialogShowing += new EventHandler<AppointmentEditDialogShowingEventArgs>(radScheduler1_AppointmentEditDialogShowing); 
    } 

    void radScheduler1_AppointmentEditDialogShowing(object sender, Telerik.WinControls.UI.AppointmentEditDialogShowingEventArgs e) 
    { 
        if (this.appointmentEditDialog == null) 
        { 
            this.appointmentEditDialog = new CustomAppointmentEditDialog(); 
        } 
        e.AppointmentEditDialog = this.appointmentEditDialog; 
    } 

I hope this helps. Do not hesitate to write me back if you have further questions.

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