フィールド初期化子は非静的フィールド、メソッド、またはプロパティを参照できません


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

なぜそれが起こり、どのように修正するのですか?

回答:


143

この行:

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

インスタンス変数を使用して別のインスタンス変数を初期化することはできません。どうして?コンパイラはこれらを再配置できるため、reminderdefaultReminderに初期化される保証がないため、上記の行でスローされる可能性があり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/...
はWouter Schut

31
いいえ、コンパイラーは初期化子を再配置できません。C#言語仕様では、「10.5.5.2インスタンスフィールドの初期化」のセクションに次のように記載されています。変数初期化子は、クラス宣言に出現するテキスト順に実行されます。これは、「10.11.2インスタンス変数初期化子」でも繰り返されます。変数初期化子は、クラス宣言に出現するテキスト順に実行されます。だからあなたの説明は間違っています。順序は固定されています。これが許可されない理由は、C#の設計者がそのようにしたかったためです。
Jeppe Stig Nielsen

partial class複数のファイルに「パーツ」があるaの場合のみ、フィールド初期化子の順序は不明確ですが、それはstaticフィールドにも
当てはまり

@WouterSchutリンクするスレッドはJavaに関するものではありませんか?!C#についても同様ですが、staticインスタンスフィールドではなくフィールドを使用します。
Jeppe Stig Nielsen

2
@Andrewまったく真実ではありません。悪い習慣を禁止するために多くの決定が下されます。理論的には実装できますが、一部は警告によって保護され、一部は明白なエラーです。そして、これはこれらのケースの1つだと思います...標準はそれがシーケンシャルであると言いますが、経験豊富な開発者でさえ自信を持って(標準を検索せずに)それを言うことはありません。
Tomer W

21

そのコードをクラスのコンストラクターに入れる必要があります。

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

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

その理由は、フィールド初期化子を使用して、1つのインスタンス変数を別のインスタンス変数を初期化するために使用できないためです。


9

このように使えます

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

10
Stack Overflowへようこそ!このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。また、コードと説明の両方の読みやすさが低下するため、コードを説明コメントで混雑させないようにしてください。
jmattheis

3
=ではなく=>を使用しているため、プロパティになります。
Vincent

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.