'private static readonly'字段和静态/非静态构造函数

时间:2015-04-21 04:19:24

标签: c# modifier

我有三个对象:

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#相对较新

5 个答案:

答案 0 :(得分:6)

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

静态成员是一个不需要存在实例的成员:它属于类",而不属于对象(类的实例)。

现在readonly修饰符表示成员只能在构造函数中(或在其声明中分配一个值,但这里不相关)。

有两种类型的构造函数:静态构造函数和实例构造函数......区别与上面的区别相同,readonly修饰符当然适用于每种类型的构造函数:{{1} }意思是"你只能在静态构造函数"中更改它的值,而实例static readonly意味着"你可以在实例构造函数中更改它的值"。

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

现在,在示例中,您只是随意将成员更改为readonly,或者只是尝试编译它。

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

因此静态构造函数中的这一行毫无意义:

static

您正在尝试访问c = new Apple(b.getDna()); ,这是一个实例成员,但您并未说明应从哪个实例获取该值。

你应该重新考虑你的设计,并思考为什么成员会是静态的,不仅仅是"移动东西并试图让它编译和运行"。

答案 1 :(得分:1)

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

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

您正在尝试将值分配给实例构造函数中的只读静态变量。在您调用实例构造函数时,已为变量分配了一个值,该值为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

  

在创建第一个实例或引用任何静态成员之前,会自动调用静态构造函数来初始化类。

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

您正面临一个非常常见的问题,即您尝试混合实例变量和静态变量。这种方法可能导致非常奇怪的行为,就像你所面临的那样。

我建议你不要混淆这些变量,使它们全部静止或者将它们全部变为实例,但不要将它们混合在一起。否则,您可能在不久的将来面临不同的问题

答案 4 :(得分:1)

  

这里有错误的警告吗?

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

  1. 声明或
  2. static构造函数中。
  3. 我们不能在实例构造函数中执行此操作。

    您的另一个问题是static字段无法依赖实例1。

      

    我该如何规避这个?

    这是creative way to circumvent it

    1. static构造函数中,指定给static _b
    2. 在实例构造函数中,将static _b分配给实例的b
    3. 我们甚至可以在完成后分配_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(); }
          }
      }
      

      它可以让你绕过这个问题。