为什么可以';t子类使用基类保护的构造函数创建新对象
本文关键字:创建 构造函数 保护 新对象 对象 基类 子类 为什么 | 更新日期: 2023-09-27 17:58:14
我正在将一些Java代码移植到C#,我遇到了这个用于复制对象的习惯用法:
class Base
{
int x;
public Base(int x) { this.x = x; }
protected Base(Base other) { x = other.x; }
}
class Derived : Base
{
Base foo;
public Derived(Derived other)
: base(other)
{
foo = new Base(other.foo); // Error CS1540
}
}
错误CS1540为:
无法通过"Base"类型的限定符访问受保护成员"Base.Base(Base)";限定符的类型必须为"Derived"(或从中派生)
我理解这个错误的目的:它阻止访问兄弟类型的受保护成员。但是Base.Base(Base)显然不会在同级类型上调用!这只是不包括在规范中,还是我遗漏了一些不安全的原因?
编辑:Gah,成语是new Base(other.foo)
而不是new Base(other)
语言规范的第3.5.2节和第3.5.3节详细说明了这一点,为了方便起见,我将发布3.5.2(它更短!),让您自己找到3.5.3。
直观地说,当访问成员M时评估步骤以确保允许访问:
- 首先,如果M是在一个类型中声明的(与编译单元相反或命名空间)、编译时错误如果该类型不可访问,则发生
- 然后,如果M是公共的,则允许访问
- 否则,如果M在内部受到保护,则允许访问它发生在程序中M被声明,或者如果它发生在从中的类派生的类M被声明并发生通过派生类类型(§3.5.3)
- 否则,如果M受到保护,则在发生访问时允许访问在M为的类内声明,或者如果它发生在类派生自其中M是通过声明和发生的派生类类型(§3.5.3)。
- 否则,如果M是内部的,则在发生访问时允许访问在M所在的程序中声明
- 否则,如果M是私有的,则在发生访问时允许访问在M是声明
- 否则,类型或成员不可访问,并且会出现编译时错误发生
基本上,必须通过派生的实例来访问受保护的基成员。正如你所说,兄弟姐妹不能访问彼此的受保护成员,但语言规范也禁止孩子访问受保护的基础成员,除非引用是通过孩子。
如果您可以执行此操作,那么您可以始终平凡地调用任何允许从中派生的类的protected
成员,即使未使用派生类。这完全颠覆了protected
机制的安全性。
例如,假设我们从Base
导出Derived
,如上所述。如果规则不是原来的样子,您可以在Derived
:中添加这样的方法
public static void CallProtectedMethod(Base baseInstance)
{
baseInstance.ProtectedMethod();
}
现在任何人都可以这样调用它:
Derived.CallProtectedMethod(baseInstance);
而直接进近失败:
baseInstance.ProtectedMethod();
在这种情况下,baseInstance
可能真的是Base
类型,并且与Derived
无关。为了防止这种情况,并确保protected
方法保持protected
,除非实例确实是Derived
类型,否则通过其他实例调用这些方法是非法的。
同样,对于protected
构造函数:
public static Base CreateProtectedBase()
{
return new Base();
}
现在任何人都可以这样调用它:
var baseInstance = Derived.CreateProtectedBase();
而直接进近失败:
var baseInstance = new Base();
它不可访问,您可以查看此帖子了解详细信息:许多问题:受保护的构造函数
如果在派生中创建一个新的基对象,则无法通过该对象访问受保护的成员。试试这个代码
class Base
{
protected int x;
}
class Derived : Base
{
Base foo;
void testMethod()
{
base.x = 5;
foo.X = 5;// this throws an error
}
}
更改构造函数的访问说明符以使其工作,或者使用base.X
来代替创建基类(base)的对象