C# 为什么可以';t子类使用基类保护的构造函数创建新对象?

C# 为什么可以';t子类使用基类保护的构造函数创建新对象?,c#,inheritance,constructor,protected,C#,Inheritance,Constructor,Protected,我正在将一些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) {

我正在将一些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)”;限定符的类型必须为“派生”(或从中派生)

我理解此错误的目的:它阻止访问兄弟类型的受保护成员。但是Base.Base(Base)显然不会在兄弟类型上被调用!这只是不包括在规范中,还是我遗漏了一些不安全的原因


编辑:嘎,习惯用法是
newbase(other.foo)
而不是
newbase(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所在的类型中 声明
  • 否则,类型或成员将不可访问,并且会出现编译时错误 发生

基本上,必须通过派生的实例访问受保护的基成员。正如您所说,兄弟姐妹不能访问彼此的受保护成员,但语言规范也禁止孩子访问受保护的基成员,除非引用是通过孩子进行的

不可访问您可以查看此帖子以了解详细信息:

如果在派生中创建新的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)的对象。如果您可以这样做,那么您可以始终轻松地调用允许您从中派生的任何类的
受保护的
成员,即使未使用派生类。这完全破坏了受保护
机制的安全性

例如,假设我们从
Base
派生出
Derived
,如上所述。如果规则不是原来的规则,您可以将类似这样的方法添加到
派生的

public static void CallProtectedMethod(Base baseInstance)
{
    baseInstance.ProtectedMethod();
}
现在任何人都可以这样调用它:

Derived.CallProtectedMethod(baseInstance);
var baseInstance = Derived.CreateProtectedBase();
虽然直接方法失败了:

baseInstance.ProtectedMethod();
var baseInstance = new Base();
在这种情况下,
baseInstance
可能确实属于
Base
类型,与
派生的
无关。为了防止这种情况,并确保
受保护的
方法保持
受保护
,除非实例确实是
派生的
类型,通过不同实例调用这些方法是非法的

同样,对于受保护的
构造函数:

public static Base CreateProtectedBase()
{
    return new Base();
}
现在任何人都可以这样调用它:

Derived.CallProtectedMethod(baseInstance);
var baseInstance = Derived.CreateProtectedBase();
虽然直接方法失败了:

baseInstance.ProtectedMethod();
var baseInstance = new Base();

@阿拉斯泰尔·皮茨——是的………你是对的,我对非构造函数成员有这样的理解,但据我所知,构造函数成员仅由
new
的实现调用-所以我看不出这怎么可能是不安全的。@Simon,我不会假装我能想到那些可能会爆炸的角落案例,但我毫不怀疑有人能想出什么办法。我只能说,通过提供受保护的方法而不是受保护的构造函数,您可以绕过这个限制
protectedbase-CreateBase(Base-other)
可以通过派生子级调用它。@Simon:想在任何库中调用
protected
构造函数吗?只要写十行代码!这就是为什么它是非法的。@Rick:我对
protected
的解释是,它有助于强制执行类型安全性,并使强制执行Liskov可替换性更容易。如果我想防止其他程序集访问成员进行安全保护,我会考虑
internal
private
。如果这不是预期用途,那就是我的答案@Simon Buchan:声明受保护的构造函数的目的不是限制谁可以创建实例,而是限制可以创建什么类型的实例。将类“foo”构造函数声明为“protected”可防止其用于创建“foo”实例,同时允许其用于创建从“foo”派生的类型。除了从派生类构造函数链接之外,通过任何方式调用任何“foo”构造函数都会生成一个“foo”实例,而不是派生类实例——这正是“protected”声明应该阻止的。感谢链接,所以受保护的构造函数被阻止了。。。作为安全保护?这很奇怪。是的……甚至我都不知道这一点,但读了这篇文章后,我看到了答案的另一面,因为它联系了推理。如果有人(时间比我多!)为其他人编辑了这个答案的理由,那就太好了。