为什么我不能访问c#受保护的成员,除了像这样?

时间:2022-09-02 09:49:17

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)

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

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?

他们到底在想什么?(改变这条规则会破坏什么吗?)除了把F公布于众之外,还有别的办法吗?


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?

为什么D不能打电话给e.f. ?


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

错误消息被编辑,因此我可能在其中输入了一个错码。

6 个解决方案

#1


17  

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.

“protected”关键字意味着只有来自该类型的类型和类型才能访问该成员。D与C没有关系,因此不能访问成员。

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
  • D来自C

EDIT

编辑

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

这个场景在c#规范的3.5.3节中被调用。

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

不允许这样做的原因是它允许跨层次结构调用。想象一下,除了D之外,还有另一个C基类e,如果你的代码可以编译,它将允许D访问成员E.F. (c#中不允许使用这种场景)。

EDIT2 Why this is bad

EDIT2,为什么这是不好的。

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.

考虑一下如果F是一个时间关键函数会发生什么。根据当前的行为,我可以推断我的类的正确性。毕竟我的课只有两种情况。就会调用F。

  1. Where it's invoked in C
  2. 在C中调用的地方
  3. Where I explicitly invoke it in MyClass
  4. 我在MyClass中显式调用它?

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

我可以检查这些调用并得出关于MyClass如何工作的合理结论。

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.

现在,如果c#允许跨层次结构保护访问,我不能保证。在完全不同的程序集中,任何人都可以从c中派生出来,然后他们可以调用MyClass。F。这使得我们完全不可能对我的课程的正确性进行推理。

#2


33  

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#不允许跨层次调用受保护的方法。假设有一个E类也来自于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.

然后参考你想调用该方法可以E类型的一个实例,因此该方法可以解决在运行时在c# E.F.这是不允许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

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.

这是有意义的,因为关键字protected意味着成员“可以在它的类和派生类实例中访问”和E。F不属于D。

#3


11  

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.

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

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

一系列文章从c#编译器的角度解释了这一点:

#4


2  

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.

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

#5


1  

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.

因此,如果编译器允许在描述的方法中从超类调用protected方法,那么我们就可以使用这个答案中所描述的保护方法的跨层级调用。在这种情况下,必须搜索定义成员的父类的所有子类。这将扩大范围。

PS. The same behavior is implemented in Java.

同样的行为在Java中实现。

#6


0  

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).
  2. 继承默认的表单(editappointment对话框)并进行定制(甚至可以使用winforms designer进行定制)。

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

公共部分类自定义对话框:editaddretmentdialog {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.

在上面的代码中,我添加了一个额外的复选框,并将约会的状态(显示时间)设置为暂定(如果未选中,则设置为暂定),如果选中,则设置为Busy。访问组合框的奇怪方式是因为它目前是私有的。这将在2009年第一季度的版本中进行修改。

  1. Subscribe to AppointmentEditDialogShowing event of RadScheduler and substitute the default form with your customized one:
  2. 订阅RadScheduler操作者的appointment tmenteditdialog显示事件,并将默认表单替换为定制表单:

private IEditAppointmentDialog appointmentEditDialog = null;

私有ieditappointment对话框appointment tmenteditdialog = 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.

我希望这可以帮助。如果你还有什么问题,请随时给我回信。

#1


17  

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.

“protected”关键字意味着只有来自该类型的类型和类型才能访问该成员。D与C没有关系,因此不能访问成员。

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
  • D来自C

EDIT

编辑

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

这个场景在c#规范的3.5.3节中被调用。

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

不允许这样做的原因是它允许跨层次结构调用。想象一下,除了D之外,还有另一个C基类e,如果你的代码可以编译,它将允许D访问成员E.F. (c#中不允许使用这种场景)。

EDIT2 Why this is bad

EDIT2,为什么这是不好的。

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.

考虑一下如果F是一个时间关键函数会发生什么。根据当前的行为,我可以推断我的类的正确性。毕竟我的课只有两种情况。就会调用F。

  1. Where it's invoked in C
  2. 在C中调用的地方
  3. Where I explicitly invoke it in MyClass
  4. 我在MyClass中显式调用它?

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

我可以检查这些调用并得出关于MyClass如何工作的合理结论。

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.

现在,如果c#允许跨层次结构保护访问,我不能保证。在完全不同的程序集中,任何人都可以从c中派生出来,然后他们可以调用MyClass。F。这使得我们完全不可能对我的课程的正确性进行推理。

#2


33  

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#不允许跨层次调用受保护的方法。假设有一个E类也来自于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.

然后参考你想调用该方法可以E类型的一个实例,因此该方法可以解决在运行时在c# E.F.这是不允许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

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.

这是有意义的,因为关键字protected意味着成员“可以在它的类和派生类实例中访问”和E。F不属于D。

#3


11  

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.

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

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

一系列文章从c#编译器的角度解释了这一点:

#4


2  

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.

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

#5


1  

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.

因此,如果编译器允许在描述的方法中从超类调用protected方法,那么我们就可以使用这个答案中所描述的保护方法的跨层级调用。在这种情况下,必须搜索定义成员的父类的所有子类。这将扩大范围。

PS. The same behavior is implemented in Java.

同样的行为在Java中实现。

#6


0  

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).
  2. 继承默认的表单(editappointment对话框)并进行定制(甚至可以使用winforms designer进行定制)。

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

公共部分类自定义对话框:editaddretmentdialog {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.

在上面的代码中,我添加了一个额外的复选框,并将约会的状态(显示时间)设置为暂定(如果未选中,则设置为暂定),如果选中,则设置为Busy。访问组合框的奇怪方式是因为它目前是私有的。这将在2009年第一季度的版本中进行修改。

  1. Subscribe to AppointmentEditDialogShowing event of RadScheduler and substitute the default form with your customized one:
  2. 订阅RadScheduler操作者的appointment tmenteditdialog显示事件,并将默认表单替换为定制表单:

private IEditAppointmentDialog appointmentEditDialog = null;

私有ieditappointment对话框appointment tmenteditdialog = 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.

我希望这可以帮助。如果你还有什么问题,请随时给我回信。