【问题标题】:'private static readonly' fields and static/nonstatic constructors“私有静态只读”字段和静态/非静态构造函数
【发布时间】:2015-06-28 01:27:36
【问题描述】:

我有三个对象:

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。如果我删除静态 只读修饰符,它会完美编译。 (这里有错误的警告吗?)

在此处查看其他答案时,我发现我应该使用静态构造函数,例如:

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

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

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

我该如何规避这个问题?

附:我对 C# 比较陌生

【问题讨论】:

  • 当您创建类的新实例并调用特定的构造函数时,通常先调用静态一个,然后调用第二个。但是启动应用程序时会调用静态构造函数
  • 先调用静态构造函数,第一次访问该类型时调用。问题是,为什么要在静态上下文中访问实例变量 (c = new Apple(b.getDna());)。这没有意义(并且会引发编译时错误)并且从远处闻到气味。要访问b,您需要一个实例,而您在静态上下文中没有一个(没有this)。你应该重新考虑你的设计和你的目标是什么static/non-static
  • 我已编辑问题以显示将首先调用静态构造函数。关于你评论的后半部分,我很快就会用我的实际代码再次编辑我的问题,以表明我遇到了这个问题。
  • 我强烈建议您更改此实现设计,因为我看到您的静态成员(c)依赖于实例成员(b),这根本不是一个好主意。

标签: c# modifier


【解决方案1】:

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

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

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

构造函数有两种类型:静态构造函数和实例构造函数……区别和上面一样,当然readonly修饰符适用于每种类型的构造函数:static readonly表示“您只能在静态构造函数中更改其值”,而实例 readonly 的意思是“您可以在实例构造函数中更改其值”。

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

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

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

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

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

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

你真的应该重新考虑你的设计,想想为什么成员会是静态的或不是静态的,而不仅仅是“移动东西并尝试让它编译和运行”。

【讨论】:

  • 说readonly是不正确的,“说一个成员只能在构造函数中赋值”。申报后也可领取。
  • @ShaunLuttin 没错,但我认为它与问题无关:我将对其进行编辑并将其添加为评论
  • 这可能与问题不完全相关,但无论如何:我的真实代码是服务器的代码。我只有一个服务器类实例,它产生多个迷你服务器(基本上,哈哈)。如果出现重大错误,任何迷你服务器都应该能够启动全局关闭。主服务器类具有启动关闭的功能。要调用它,我可以将关闭函数的副本发送到每个迷你服务器,或者使服务器关闭静态,我这样做是因为它更简单。续...
  • 现在我意识到只有将我的整个主服务器设置为静态才有意义,因为无论如何它只会存在一个副本。但是如果我要让主服务器成为一个实例类,我将如何解决这个问题呢?
  • @Hele 这不会在评论中出现,并且需要示例来思考正确的设计......有很多方法可以实现你想要的
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

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

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

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

      【讨论】:

        【解决方案5】:

        这里有错误的警告吗?

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

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

        我们不能在实例构造函数中这样做。

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

        我该如何规避这个问题?

        这是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(); }
            }
        }
        

        它确实可以让你规避问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-15
          • 1970-01-01
          • 1970-01-01
          • 2014-02-26
          • 1970-01-01
          • 2016-08-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多