【问题标题】:A field initializer cannot reference the nonstatic field, method, or property字段初始值设定项不能引用非静态字段、方法或属性
【发布时间】:2013-01-04 13:01:26
【问题描述】:

我有一个班级,当我尝试在另一个班级中使用它时,我收到以下错误。

using System;
using System.Collections.Generic;
using System.Linq;

namespace MySite
{
    public class Reminders
    {
        public Dictionary<TimeSpan, string> TimeSpanText { get; set; }

        // We are setting the default values using the Costructor
        public Reminders()
        {
            TimeSpanText.Add(TimeSpan.Zero, "None");
            TimeSpanText.Add(new TimeSpan(0, 0, 5, 0), "5 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 15, 0), "15 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 0, 30, 0), "30 minutes before");
            TimeSpanText.Add(new TimeSpan(0, 1, 0, 0), "1 hour before");
            TimeSpanText.Add(new TimeSpan(0, 2, 0, 0), "2 hours before");
            TimeSpanText.Add(new TimeSpan(1, 0, 0, 0), "1 day before");
            TimeSpanText.Add(new TimeSpan(2, 0, 0, 0), "2 day before");
        }

    }
}

在另一个类中使用该类

class SomeOtherClass
{  
    private Reminders reminder = new Reminders();
    // error happens on this line:
    private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
    ....

错误(CS0236):

A field initializer cannot reference the nonstatic field, method, or property

为什么会发生以及如何解决?

【问题讨论】:

    标签: c#


    【解决方案1】:

    这一行:

    private dynamic defaultReminder = 
                              reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
    

    您不能使用实例变量来初始化另一个实例变量。为什么?因为编译器可以重新排列这些 - 不能保证 reminder 将在 defaultReminder 之前初始化,所以上面的行可能抛出一个 NullReferenceException

    相反,只需使用:

    private dynamic defaultReminder = TimeSpan.FromMinutes(15);
    

    或者,在构造函数中设置值:

    private dynamic defaultReminder;
    
    public Reminders()
    {
        defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
    }
    

    MSDN 上有关于此编译器错误的更多详细信息 - Compiler Error CS0236

    【讨论】:

    • Java 对这类结构更加“宽容”。不知道这是不是一件好事。 stackoverflow.com/questions/1494735/…
    • 不,编译器无法重新排列初始化程序。 C# 语言规范在“10.5.5.2 实例字段初始化”部分规定如下:变量初始化程序按照它们在类声明中出现的文本顺序执行。甚至重复在“10.11.2 实例变量初始化程序”中,他们说:变量初始化程序按照它们在类声明中出现的文本顺序执行。所以你的解释是错误的。顺序是固定的。不允许它的原因是 C# 的设计者想要这样。
    • (仅在 partial class 的情况下,在多个文件中带有“部分”)字段初始值设定项的顺序不清楚,但 static 字段也是如此!)
    • @WouterSchut 您链接的线程与 Java 无关?!它也是关于 C# 的,但是使用 static 字段而不是实例字段。
    • @Andrew 完全不正确,许多决定都是为了禁止不良做法。尽管理论上它们可以实现,但有些受到警告的保护,有些是简单的错误。我认为这是其中一种情况......即使标准说它是连续的,即使是有经验的开发人员也不会自信地说出来(不搜索标准)。
    【解决方案2】:

    您需要将该代码放入类的构造函数中:

    private Reminders reminder = new Reminders();
    private dynamic defaultReminder;
    
    public YourClass()
    {
        defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
    }
    

    原因是您不能使用一个实例变量来使用字段初始化器来初始化另一个实例变量。

    【讨论】:

      【解决方案3】:

      你可以这样使用

      private dynamic defaultReminder => reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 
      

      【讨论】:

      • 欢迎来到 Stack Overflow!虽然这段代码 sn-p 可以解决问题,但包含 explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性 cmets 挤满你的代码,因为这会降低代码和解释的可读性!
      • 它使用 => 而不是 = 从而使其成为一个属性。
      • 小心使用这种技术,因为使用=&gt; 不会设置实际值,而是会在每次访问defaultReminder 时执行代码。这可能不是故意的,并且会对性能产生负面影响,或者对 GC 等产生不必要的压力。
      【解决方案4】:

      private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; 是一个字段初始值设定项并首先执行(在任何没有初始值设定项的字段设置为其默认值之前以及在调用的实例构造函数执行之前)。没有初始化器的实例字段只有在所有实例字段初始化器完成后才具有合法(默认)值。由于初始化顺序,实例构造函数最后执行,这就是为什么在执行初始化程序时还没有创建实例的原因。因此,编译器不允许在类实例完全构造之前引用任何实例属性(或字段)。这是因为对 reminder 等实例变量的任何访问都会隐式引用实例 (this) 以告诉编译器要使用的实例的具体内存位置。

      这也是为什么 this 不允许在实例字段初始化器中的原因。

      实例字段的变量初始化器不能引用 正在创建的实例。因此,引用是编译时错误 this 在变量初始化器中,因为它是一个编译时错误 变量初始化器通过 a 引用任何实例成员 simple_name

      唯一保证在实例字段初始化器执行之前被初始化的类型成员是类(静态)字段初始化器和类(静态)构造函数和类方法。由于静态成员是独立于实例的,因此可以随时引用它们:

      class SomeOtherClass
      {
        private static Reminders reminder = new Reminders();
      
        // This operation is allowed,
        // since the compiler can guarantee that the referenced class member is already initialized
        // when this instance field initializer executes
        private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];
      }
      

      这就是为什么实例字段初始化器只允许引用一个类成员(静态成员)。此编译器初始化规则将确保确定性类型实例化。

      如需了解更多详情,我推荐此文档:Microsoft Docs: Class declarations

      这意味着引用另一个实例成员来初始化其值的实例字段必须从实例构造函数中初始化,或者必须将引用的成员声明为static

      【讨论】:

        猜你喜欢
        • 2011-11-16
        • 2022-01-23
        相关资源
        最近更新 更多