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

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


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

#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上此编译器错误的详细信息-编译器错误CS0236。

评论


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

– Wouter Schut
15年2月3日,11:52

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

–杰普·斯蒂格·尼尔森(Jeppe Stig Nielsen)
16年4月18日在20:16

(只有在几个文件中带有“ parts”的局部类的情况下,字段初始化程序的顺序才不清楚,但这也适用于静态字段!)

–杰普·斯蒂格·尼尔森(Jeppe Stig Nielsen)
16年4月18日在20:18

@WouterSchut您链接的线程不是Java吗?它也与C#有关,但是具有静态字段而不是实例字段。

–杰普·斯蒂格·尼尔森(Jeppe Stig Nielsen)
16年4月18日在20:33

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

– Tomer W
17年7月7日在18:42

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

– jmattheis
16年8月6日在19:46

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

– Vincent
16年12月24日在1:15

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

– Smilediver
4月16日18:22

#4 楼

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