Un inizializzatore di campo non può fare riferimento al campo, al metodo o alla proprietà non statici


92

Ho una classe e quando provo a usarla in un'altra classe ricevo l'errore seguente.

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

    }
}

Usare la classe in un'altra classe

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

Errore (CS0236):

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

Perché succede e come risolverlo?

Risposte:


143

Questa linea:

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

Non è possibile utilizzare una variabile di istanza per inizializzare un'altra variabile di istanza. Perché? Poiché il compilatore può riorganizzarli, non vi è alcuna garanzia che reminderverrà inizializzato prima defaultReminder, quindi la riga sopra potrebbe generare un file NullReferenceException.

Invece, usa solo:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

In alternativa, imposta il valore nel costruttore:

private dynamic defaultReminder;

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

Sono disponibili ulteriori dettagli su questo errore del compilatore su MSDN - Errore del compilatore CS0236 .


3
Java è più "indulgente" per questo tipo di costrutti. Non so se sia una buona cosa. stackoverflow.com/questions/1494735/…
Wouter Schut

31
No, il compilatore non può riorganizzare gli inizializzatori. La specifica del linguaggio C # afferma, nella sezione "10.5.5.2 Inizializzazione del campo di istanza", quanto segue: Gli inizializzatori di variabili vengono eseguiti nell'ordine testuale in cui appaiono nella dichiarazione di classe. Questo viene ripetuto anche in "10.11.2 Inizializzatori di variabili di istanza" dove si dice: Gli inizializzatori di variabili vengono eseguiti nell'ordine testuale in cui appaiono nella dichiarazione di classe. Quindi la tua spiegazione è sbagliata. L'ordine è fisso. Il motivo per cui non è consentito è che i progettisti di C # lo volevano in questo modo.
Jeppe Stig Nielsen

(Solo nel caso di un partial classcon "parti" in più file l'ordine degli inizializzatori di campo non è chiaro, ma questo vale anche per i staticcampi!)
Jeppe Stig Nielsen

@WouterSchut Il thread che colleghi non riguarda Java ?! Si tratta anche di C #, tuttavia con staticcampi anziché campi di istanza.
Jeppe Stig Nielsen

2
@Andrew Non è affatto vero, vengono prese molte decisioni per vietare le cattive pratiche. anche se possono, in teoria, essere implementati, alcuni protetti da avvertimenti e altri sono semplici errori. e penso che questo sia uno di questi casi ... anche se lo standard dice che è sequenziale, anche uno sviluppatore esperto non lo direbbe con sicurezza (senza cercare lo standard).
Tomer W

22

Devi inserire quel codice nel costruttore della tua classe:

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

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

Il motivo è che non è possibile utilizzare una variabile di istanza per inizializzarne un'altra utilizzando un inizializzatore di campo.


9

puoi usare in questo modo

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

10
Benvenuto in Stack Overflow! Sebbene questo snippet di codice possa risolvere la domanda, includere una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice. Si prega inoltre di cercare di non riempire il codice con commenti esplicativi, poiché ciò riduce la leggibilità sia del codice che delle spiegazioni!
jmattheis

3
Sta usando => invece di = rendendolo così una proprietà.
Vincent

2
Fai attenzione a usare questa tecnica, poiché l'uso =>non imposta il valore effettivo, ma eseguirà il codice ogni volta che defaultRemindersi accede. Ciò potrebbe non essere inteso e avere un impatto negativo sulle prestazioni o generare una pressione indesiderata per GC, ecc.
Smilediver

5

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];è un inizializzatore di campo e viene eseguito per primo (prima che qualsiasi campo senza inizializzatore sia impostato sul valore predefinito e prima che venga eseguito il costruttore dell'istanza invocato). I campi dell'istanza che non hanno inizializzatore avranno solo un valore legale (predefinito) dopo che tutti gli inizializzatori dei campi dell'istanza saranno stati completati. A causa dell'ordine di inizializzazione, i costruttori di istanza vengono eseguiti per ultimi, motivo per cui l'istanza non viene ancora creata nel momento in cui vengono eseguiti gli inizializzatori. Pertanto il compilatore non può consentire di fare riferimento a nessuna proprietà dell'istanza (o campo) prima che l'istanza della classe sia completamente costruita. Questo perché qualsiasi accesso a una variabile di istanza come fa reminderriferimento implicitamente a instance ( this) per indicare al compilatore la posizione di memoria concreta dell'istanza da utilizzare.

Questo è anche il motivo per cui thisnon è consentito in un inizializzatore di campo di istanza.

Un inizializzatore di variabile per un campo istanza non può fare riferimento all'istanza che si sta creando. Pertanto, è un errore in fase di compilazione fare riferimento a questo in un inizializzatore di variabile, poiché è un errore in fase di compilazione per un inizializzatore di variabile fare riferimento a qualsiasi membro dell'istanza tramite un simple_name .

Gli unici membri di tipo di cui è garantita l'inizializzazione prima dell'esecuzione degli inizializzatori di campo dell'istanza sono gli inizializzatori di campo (statico) di classe, i costruttori di classe (statici) ei metodi di classe. Poiché i membri statici sono indipendenti dall'istanza, è possibile fare riferimento in qualsiasi momento:

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

Ecco perché gli inizializzatori di campo dell'istanza possono solo fare riferimento a un membro della classe (membro statico). Queste regole di inizializzazione del compilatore garantiranno un'istanza di tipo deterministico.

Per maggiori dettagli consiglio questo documento: Microsoft Docs: Class dichiarations .

Ciò significa che un campo dell'istanza che fa riferimento a un altro membro dell'istanza per inizializzarne il valore, deve essere inizializzato dal costruttore dell'istanza oppure il membro a cui si fa riferimento deve essere dichiarato static.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.