字段初始值设定项不能引用非静态字段,方法或属性


92

我有一个课程,当我尝试在另一个课程中使用它时,出现以下错误。

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

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

Answers:


143

这行:

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-编译器错误CS0236


3
Java对于此类构造更为“宽容”。不知道那是否是好事。stackoverflow.com/questions/1494735/...
沃特SCHUT

31
不,编译器无法重新排列初始化程序。C#语言规范在“ 10.5.5.2实例字段初始化”部分中指出以下内容:变量初始化程序按照它们出现在类声明中的文本顺序执行。甚至在“ 10.11.2实例变量初始值设定项”中也重复了这样的说法:变量初始值设定项是以它们出现在类声明中的文本顺序执行的。所以你的解释是错误的。订单是固定的。之所以禁止使用它,是因为C#的设计者就是这么想的。
Jeppe Stig Nielsen

(仅在partial class几个文件中带有“ parts” 的情况下,字段初始化程序的顺序不清楚,但这也适用于static字段!)
Jeppe Stig Nielsen

@WouterSchut您链接的线程不是Java吗?它也与C#有关,但是使用static字段而不是实例字段。
Jeppe Stig Nielsen

2
@Andrew完全不对,做出了许多禁止不良行为的决定。即使从理论上讲,它们可以实现,但有些受到警告的保护,而有些则是纯错误。我认为这是其中一种情况...即使标准说它是连续的,即使是经验丰富的开发人员也不会自信地说(不搜索标准)。
Tomer W

21

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

private Reminders reminder = new Reminders();
private dynamic defaultReminder;

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

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


9

你可以这样使用

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

10
欢迎使用Stack Overflow!尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。也请尽量不要在代码中添加解释性注释,因为这会降低代码和解释的可读性!
jmattheis

3
它使用=>代替=,从而使其成为一个属性。
文森特

2
使用此技术时要小心,因为使用=>不会设置实际值,但是每次defaultReminder访问都会执行代码。这可能不是预期的,并具有性能产生负面影响,或产生不必要的压力,GC等
Smilediver

5

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

这也是为什么this实例字段初始化程序中不允许使用的原因。

实例字段的变量初始化器无法引用正在创建的实例。因此,在变量初始值设定项中引用它是编译时错误,因为对于变量初始值设定项通过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:类声明

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.