'private static readonly' 个字段和 static/nonstatic 个构造函数

'private static readonly' fields and static/nonstatic constructors

我有三个对象:

private static readonly Apple a, c;
private readonly Orange b;

这段代码是从我的构造函数中调用的:

public SomeClass()
{
    a = new Apple();
    b = new Orange(a.getDna());
    c = new Apple(b.getDna());
}

它给我错误 Readonly field cannot be used as an assignment target。如果我删除静态 只读修饰符,它会完美编译。 (这里有错误警告吗?)

在 SO 上检查其他答案时,我发现我应该使用像这样的静态构造函数:

static SomeClass()
{
    a = new Apple();
    c = new Apple(b.getDna());
}

public SomeClass()
{
    b = new Orange(a.getDna());
}

但这会导致首先调用静态构造函数并导致错误,因为 b 不会被初始化。

我该如何避免这种情况?

P.S。我对 C# 比较陌生

错误消息实际上是正确的。

首先,静态意味着它属于class。非静态意味着它是每个实例。实例方法可以修改静态变量,但是静态方法不能修改实例变量(会修改哪个实例?)

鉴于此,只读意味着您只能在创建期间进行初始化(例如构造函数。)

您收到错误是因为您试图在实例构造函数中创建一个只读静态变量之后对其进行分配。

让我们从定义什么是 static 以及 staticinstance 成员之间的区别开始。

静态成员是不需要实例存在的成员:它 "belongs to the class",而不是对象(class 的实例)。

现在 readonly 修饰符表示只能在构造函数中(或在其声明中,但此处不相关)为成员赋值。

构造函数有两种类型:静态构造函数和实例构造函数...区别和上面的区别一样,当然还有readonly修饰符,应用于每一种构造函数:static readonly 表示 "you can only change its value in the static constructor",实例 readonly 表示 "you can change its value in the instance constructor".

第一次访问类型时调用静态构造函数,因此它总是先被调用。

现在,在示例中,您只是将成员随机更改为 static 或不尝试是否编译。

想一想...在 static 上下文中您根本没有实例,因此无法访问 static 构造函数中的实例成员...此外,当静态构造函数被调用时,你不可能有任何初始化的实例,即使是外部定义的,因为它会 always 在你有机会初始化一个之前被调用。

所以静态构造函数中的这一行没有意义:

c = new Apple(b.getDna());

您正在尝试访问 b,这是一个实例成员,但您没有说明应该从哪个实例获取值。

你真的应该重新考虑你的设计,想想为什么成员是静态的或不是静态的,而不仅仅是 "move things around and try to make it compile and run"。

错误是正确的,因为如果您创建 SomeClass 的另一个实例,静态字段 a 将被分配两次,违反只读约束。

您正在尝试为实例构造函数中的只读静态变量赋值。在您调用实例构造函数时,变量已经分配了一个值,该值是 null。

public SomeClass()
{
    a = new Apple(); // it is already assigned as null. 
    b = new Orange(a.getDna()); //there is no value assigned to it yet
    c = new Apple(b.getDna()); //it is already assigned as null
}

发生这种情况是因为在实例构造函数之前调用了静态构造函数。您可以了解更多详情 here:

A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.

但是,当您尝试访问静态构造函数中的实例变量时,您会遇到问题。此时,尚未调用实例构造函数,这意味着您的变量 b 尚未初始化。

您遇到了一个非常普遍的问题,您试图混合使用实例变量和静态变量。这种方法可能会导致非常奇怪的行为,就像您所面临的那样。

我建议你不要混合这些变量,将它们全部设为静态或将它们全部设为实例,但不要将其混合。否则,您可能会在不久的将来遇到不同的问题

Is there a wrong warning here?

没有。警告是正确的。 如果一个字段是 readonly,我们可以在两个地方给它赋值:在声明时或在构造函数中。此外,如果某物是 static,则其关联的构造函数也是 static。因此,我们可以在两个地方分配给 staticreadonly 字段:

  1. 申报时或
  2. static 构造函数中。

我们不能在实例构造函数中执行此操作。

你的另一个问题是关于 static 字段不能依赖于一个实例。

How do I circumvent this?

这是一个creative way to circumvent it:

  1. static构造函数中,赋值给static _b
  2. 在实例构造函数中,将static _b赋值给实例的b

我们甚至可以在完成后分配 _b = null 并仍然访问我们之前分配给 b 的值。

这是一个例子:

public class SomeClass
{
    private static readonly Apple a, c;
    private static Orange _b;
    private readonly Orange b;

    static SomeClass()
    {
        a = new Apple();        
        _b = new Orange(a.getDna());
        c = new Apple(_b.getDna());
    }

    public SomeClass()
    {
        b = _b;
        _b = null;
    }

    //
    // the rest is just class definitions
    //      

    public class Apple
    {
        public Apple(object o = null) {}
        public object getDna() { return new object(); }
    }

    public class Orange
    {
        public Orange(object o = null) { }
        public object getDna() { return new object(); }
    }
}

它确实能让你规避问题。