.net 为什么我不能从子类中调用受保护的构造函数?

3phpmpom  于 2023-03-04  发布在  .NET
关注(0)|答案(2)|浏览(129)

我正在将一个VB.NET项目转换为C#。有一个代码块在转换时,有一个调用超类中受保护构造函数的方法。我在这里做了一个最小的例子:

public class A
{
    protected A() { }
}

public class B : A
{
    static A MakeA() {
        return new A();
    }
}

new A()上,我看到"CS0122:A. A()因其保护级别而无法访问"。
现在,这对我来说是一个问题,因为类A在一个我无法控制的库中,我们的VB.NET代码调用这个构造函数没有问题,但C#有问题。
为什么会有这种事?
我能做些什么呢?

zpf6vheq

zpf6vheq1#

这在C#规范(ECMA-334)中被调用,并给出了原因 (我的粗体)

7.5.4受保护的访问

protectedprivate protected示例成员在声明它的类的程序文本之外被访问时,以及当protected internal示例成员在声明它的程序的程序文本之外被访问时,访问应在从声明它的类派生的类声明内发生。此外,访问必须通过该派生类类型的示例或从其构造的类类型来进行。2此限制防止一个派生类访问其他派生类的受保护成员,即使这些成员是从同一基类继承的。
虽然它对构造函数没有太大的影响,但是它对常规方法有很大的影响。

public abstract class Animal
{
    protected abstract void DoThing();
}

public class Dog : Animal
{
    protected override void DoThing() => WagTail();

    private void WagTail()
    {
        // Whatever
    }
}

public class EvilCat : Animal
{
    public static void WagDogsTail(Dog dog) => dog.DoThing();

    public static void MeowCat(EvilCat cat) => cat.DoThing();

    protected override void DoThing() => Meow();

    private void Meow()
    {
        // Whatever
    }
}

你必须使用你自己的类型的示例来做这件事,所以MeowCat是允许的,但WagDogsTail不行。因此构造函数只能通过你自己的派生类的示例来调用,所以你不能直接使用new()来调用它,只能从另一个构造函数中使用base()
在ECMA-335(.NET规范)中也提到了这一点:

8.5.3.2成员和嵌套类型的可访问性
family-支持相同类型的引用对象可以访问(例如,一个确切的类型和从它继承的所有类型)对于可验证的代码(参见§8.8),还有一个额外的要求,可能需要运行时检查:**引用应该通过一个其确切类型支持被引用者确切类型的项来进行。**也就是说,其成员正在被访问的项应该从执行访问的类型继承。

wb1gzix0

wb1gzix02#

你需要理解为什么基类要使用一个受保护的构造函数。它可能会提供某种工厂方法来创建基类的示例,你应该使用它。
通过简单地返回派生类的一个示例,在派生类中可以在某种程度上颠覆这一点:

public class B : A
{
    public static A MakeA()
    {
        return new B();
    }
}

这可能是一个非常糟糕的主意。
想象一下,如果A的实现依赖于一些特殊的代码,这些代码在受保护的构造函数完成之后初始化A的一些字段,例如:

public class A
{
    public static A Create()
    {
        return new A
        {
            _mustBeInitialised = 42
        };
    }

    protected A() {}

    public int Func(int value)
    {
        return value / _mustBeInitialised;
    }

    int _mustBeInitialised;
}

有了这两个类,代码就可以做到这一点:

A a = B.MakeA();
Console.WriteLine(a.Func(10));

结果很糟糕。
因此,您必须找出A的示例是如何正确创建的,然后使用它。

相关问题