除了这样,为什么我不能访问 C# 受保护的成员?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/567705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't I access C# protected members except like this?
提问by BCS
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”类型的限定符访问受保护成员“CF(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:
编辑:我现在明白为什么会这样了(感谢Greg),但我对理性仍然有点困惑;给出:
class E : C
{
protected override void F(D d) { }
}
Why shouldn'tD be able to be able to call E.F?
为什么不应该D是能够能够调用EF?
The error message is edited so I might have put a typo in there.
错误消息被编辑,所以我可能在那里输入了一个错字。
采纳答案by JaredPar
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
- 公开
- 使其成为内部。这将允许任何类型访问同一程序集中的成员(或其他程序集,如果您添加朋友的)
- 从 C 推导出 D
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 believethe CLR but I don't 100% know).
不允许这样做的原因是因为它允许跨层次结构调用。想象一下,除了 D 之外,还有另一个 C 基类称为 E。如果您的代码可以编译,它将允许 D 访问成员 EF 这种类型的场景在 C# 中是不允许的(我相信CLR,但我不t 100% 知道)。
EDIT2Why 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 是一个有点时间关键的函数会发生什么。根据当前的行为,我可以推断我的类的正确性。毕竟只有两种情况会调用 MyClass.F。
- Where it's invoked in C
- Where I explicitly invoke it in MyClass
- 在 C 中调用它的地方
- 我在 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 过来并从 C 派生。然后他们可以随意调用 MyClass.F。这使得完全无法推理我的课程的正确性。
回答by Greg Beech
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
因此该方法可以在运行时解析为E.F
. 这在 C# 中是不允许的,因为D
cannot callE
的受保护方法,因为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
意味着成员“可在其类内和派生类实例中访问”,而 EF 不是 D 的成员。
回答by MSN
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# 编译器的角度解释了这一点:
回答by wheleph
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 publicmember 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 protectedmember one needs to search through the container class and all its subclasses
- to find all usages of privatemember 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.
附注。Java 中实现了相同的行为。
回答by Athari
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
这个技巧可能很有用。
回答by bensuwait
Yes, it is possible. We will most probably have such an example very soon.
对的,这是可能的。我们很可能很快就会有这样的例子。
To do that you must do the following:
为此,您必须执行以下操作:
- Inherit the default form (EditAppointmentDialog) and do your customization (you can even use the winforms designer for that).
- 继承默认表单 (EditAppointmentDialog) 并进行自定义(您甚至可以为此使用 winforms 设计器)。
public partial class CustomAppointmentEditDialog : EditAppointmentDialog { private RadComboBox cmbShowTimeAs = null;
公共部分类 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.
在上面的代码中,我添加了一个额外的复选框,如果未选中,则将约会的状态(显示时间为)设置为暂定,如果选中则设置为忙碌。访问组合框的奇怪方式是因为它目前是私有的。这将在即将发布的 2009 年第一季度版本中进行更改。
- Subscribe to AppointmentEditDialogShowing event of RadScheduler and substitute the default form with your customized one:
- 订阅 RadScheduler 的 AppointmentEditDialogShowing 事件并用您自定义的表单替换默认表单:
private IEditAppointmentDialog appointmentEditDialog = null;
私人 IEditAppointmentDialog 约会EditDialog = 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.
我希望这有帮助。如果您有其他问题,请随时给我回信。
回答by Parziphal
Simply put: accessing an instance'sprotected 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.
您认为您可以访问,p.foo
因为您在子类中,但您是从实例访问它,这就像公共访问,因此被拒绝。
You're allowed to access protected
members from within the class, not from an instance (yes, we know this):
您可以protected
从类中访问成员,而不是从实例中访问成员(是的,我们知道这一点):
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:
最后,为了完整起见,您实际上只能在同一个类中访问实例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;
}
}