Penginisialisasi bidang tidak dapat mereferensikan bidang, metode, atau properti nonstatis


96

Saya memiliki kelas dan ketika saya mencoba menggunakannya di kelas lain saya menerima kesalahan di bawah ini.

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");
        }

    }
}

Menggunakan kelas di kelas lain

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

Kesalahan (CS0236):

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

Mengapa itu terjadi dan bagaimana cara memperbaikinya?

Jawaban:


147

Garis ini:

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

Anda tidak dapat menggunakan variabel contoh untuk menginisialisasi variabel contoh lain . Mengapa? Karena kompilator dapat mengatur ulang ini - tidak ada jaminan bahwa reminderakan diinisialisasi sebelumnya defaultReminder, jadi baris di atas mungkin memunculkanNullReferenceException .

Sebagai gantinya, gunakan saja:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Atau, siapkan nilai di konstruktor:

private dynamic defaultReminder;

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

Ada detail lebih lanjut tentang kesalahan kompiler ini di MSDN - Kesalahan Kompilator CS0236 .


3
Java lebih 'memaafkan' untuk konstruksi semacam ini. Tidak tahu apakah itu hal yang baik. stackoverflow.com/questions/1494735/…
Wouter Schut

32
Tidak, kompilator tidak dapat mengatur ulang penginisialisasi. Spesifikasi Bahasa C # menyatakan, di bawah bagian "10.5.5.2 Inisialisasi kolom instance", berikut ini: Penginisialisasi variabel dijalankan dalam urutan tekstual di mana mereka muncul di deklarasi kelas. Ini bahkan diulangi dalam "10.11.2 Penginisialisasi variabel contoh" di mana mereka mengatakan: Penginisialisasi variabel dieksekusi dalam urutan tekstual di mana mereka muncul di deklarasi kelas. Jadi penjelasan Anda salah. Urutan sudah diperbaiki. Alasan mengapa tidak diizinkan adalah karena para desainer C # menginginkannya seperti itu.
Jeppe Stig Nielsen

(Hanya dalam kasus a partial classdengan "bagian" di beberapa file, urutan penginisialisasi bidang tidak jelas, tetapi itu juga berlaku untuk staticbidang!)
Jeppe Stig Nielsen

@WouterSchut Utas yang Anda tautkan bukan tentang Java ?! Ini tentang C # juga, namun dengan staticbidang, bukan bidang contoh.
Jeppe Stig Nielsen

2
@Andrew Sama sekali tidak benar, Banyak keputusan dibuat untuk melarang praktik buruk. meskipun secara teoritis dapat diimplementasikan, beberapa dilindungi oleh Peringatan, dan beberapa lainnya adalah Kesalahan biasa. dan saya pikir ini adalah salah satu kasus ini ... meskipun standar mengatakan itu berurutan, bahkan pengembang berpengalaman tidak akan mengatakannya dengan percaya diri (tanpa mencari standar).
Tomer W

22

Anda perlu memasukkan kode itu ke dalam konstruktor kelas Anda:

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

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

Alasannya adalah Anda tidak dapat menggunakan satu variabel contoh untuk menginisialisasi variabel lain menggunakan penginisialisasi bidang.


10

Anda bisa menggunakan seperti ini

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

11
Selamat datang di Stack Overflow! Meskipun cuplikan kode ini dapat menyelesaikan pertanyaan, menyertakan penjelasan sangat membantu meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa mendatang, dan orang-orang itu mungkin tidak tahu alasan saran kode Anda. Harap juga mencoba untuk tidak memenuhi kode Anda dengan komentar penjelasan, karena ini mengurangi keterbacaan kode dan penjelasannya!
jmattheis

3
Ini menggunakan => bukannya = sehingga membuatnya menjadi properti.
Vincent

3
Hati-hati menggunakan teknik ini, karena menggunakan =>tidak menyetel nilai sebenarnya, tetapi akan mengeksekusi kode setiap kali defaultReminderdiakses. Ini mungkin tidak diinginkan, dan memiliki dampak kinerja negatif, atau menghasilkan tekanan yang tidak diinginkan untuk GC, dll.
Smilediver

4

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];adalah penginisialisasi bidang dan dijalankan terlebih dahulu (sebelum bidang apa pun tanpa penginisialisasi disetel ke nilai defaultnya dan sebelum konstruktor instance yang dipanggil dijalankan). Bidang instance yang tidak memiliki penginisialisasi hanya akan memiliki nilai legal (default) setelah semua penginisialisasi bidang instance dilengkapi. Karena urutan inisialisasi, konstruktor instance dijalankan terakhir, itulah sebabnya instance belum dibuat saat penginisialisasi dijalankan. Oleh karena itu, kompilator tidak dapat mengizinkan properti instance (atau bidang) apa pun untuk direferensikan sebelum instance kelas dibuat sepenuhnya. Ini karena setiap akses ke variabel instance seperti remindersecara implisit mereferensikan instance ( this) untuk memberi tahu compiler lokasi memori konkret dari instance yang akan digunakan.

Ini juga alasan mengapa thistidak diizinkan dalam penginisialisasi bidang contoh.

Penginisialisasi variabel untuk bidang instance tidak dapat mereferensikan instance yang sedang dibuat. Jadi, ini adalah kesalahan waktu kompilasi untuk mereferensikan ini dalam penginisialisasi variabel, karena ini adalah kesalahan waktu kompilasi untuk penginisialisasi variabel untuk mereferensikan setiap anggota instance melalui simple_name .

Satu-satunya anggota jenis yang dijamin akan diinisialisasi sebelum penginisialisasi kolom instance dijalankan adalah penginisialisasi kolom kelas (statis) dan konstruktor kelas (statis) serta metode kelas. Karena anggota statis adalah instance independen, mereka dapat direferensikan kapan saja:

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)];
}

Itulah mengapa penginisialisasi bidang instance hanya diizinkan untuk mereferensikan anggota kelas (anggota statis). Aturan inisialisasi compiler ini akan memastikan instansiasi tipe deterministik.

Untuk lebih jelasnya saya merekomendasikan dokumen ini: Microsoft Docs: Class declarations .

Ini berarti bahwa bidang instance yang mereferensikan anggota instance lain untuk menginisialisasi nilainya, harus diinisialisasi dari konstruktor instance atau anggota yang direferensikan harus dideklarasikan static.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.