Un initialiseur de champ ne peut pas référencer le champ, la méthode ou la propriété non statique


92

J'ai une classe et lorsque j'essaie de l'utiliser dans une autre classe, je reçois l'erreur ci-dessous.

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

    }
}

Utiliser la classe dans une autre classe

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

Erreur (CS0236):

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

Pourquoi cela arrive-t-il et comment y remédier?

Réponses:


143

Cette ligne:

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

Vous ne pouvez pas utiliser une variable d'instance pour initialiser une autre variable d'instance. Pourquoi? Parce que le compilateur peut les réorganiser - il n'y a aucune garantie qui remindersera initialisée avant defaultReminder, donc la ligne ci-dessus peut lancer un NullReferenceException.

Au lieu de cela, utilisez simplement:

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Vous pouvez également configurer la valeur dans le constructeur:

private dynamic defaultReminder;

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

Il y a plus de détails sur cette erreur du compilateur sur MSDN - Erreur du compilateur CS0236 .


3
Java est plus «indulgent» pour ce type de constructions. Je ne sais pas si c'est une bonne chose. stackoverflow.com/questions/1494735/…
Wouter Schut

31
Non, le compilateur ne peut pas réorganiser les initialiseurs. La spécification du langage C # indique, dans la section "10.5.5.2 Initialisation du champ d'instance", ce qui suit: Les initialiseurs de variable sont exécutés dans l'ordre textuel dans lequel ils apparaissent dans la déclaration de classe. Ceci est même répété dans "10.11.2 Initialiseurs de variable d'instance" où ils disent: Les initialiseurs de variable sont exécutés dans l'ordre textuel dans lequel ils apparaissent dans la déclaration de classe. Votre explication est donc erronée. L'ordre est fixe. La raison pour laquelle il est interdit est que les concepteurs de C # l'ont voulu ainsi.
Jeppe Stig Nielsen

(Ce n'est que dans le cas d'un partial classavec des "parties" dans plusieurs fichiers que l'ordre des initialiseurs de champ n'est pas clair, mais cela vaut aussi pour les staticchamps!)
Jeppe Stig Nielsen

@WouterSchut Le fil de discussion que vous liez ne concerne pas Java?! Il s'agit également de C #, mais avec des staticchamps au lieu de champs d'instance.
Jeppe Stig Nielsen

2
@Andrew Pas vrai du tout, de nombreuses décisions sont prises pour interdire les mauvaises pratiques. même s'ils peuvent, en théorie, être implémentés, certains sont protégés par des avertissements et certains sont des erreurs simples. et je pense que c'est l'un de ces cas ... même si le standard dit que c'est séquentiel, même un développeur expérimenté ne le dirait pas avec confiance (sans chercher le standard).
Tomer W

22

Vous devez mettre ce code dans le constructeur de votre classe:

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

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

La raison est que vous ne pouvez pas utiliser une variable d'instance pour en initialiser une autre à l'aide d'un initialiseur de champ.


9

tu peux utiliser comme ça

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

10
Bienvenue dans Stack Overflow! Bien que cet extrait de code puisse résoudre la question, inclure une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question aux lecteurs à l'avenir, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas surcharger votre code avec des commentaires explicatifs, car cela réduit la lisibilité du code et des explications!
jmattheis

3
Il utilise => au lieu de =, ce qui en fait une propriété.
Vincent

2
Soyez prudent en utilisant cette technique, car l'utilisation =>ne définit pas la valeur réelle, mais exécutera le code à chaque fois que vous defaultRemindery accéderez. Cela peut ne pas être intentionnel et avoir un impact négatif sur les performances, ou générer une pression indésirable pour GC, etc.
Smilediver

5

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)];est un initialiseur de champ et s'exécute en premier (avant qu'un champ sans initialiseur ne soit défini sur sa valeur par défaut et avant que le constructeur d'instance appelé ne soit exécuté). Les champs d'instance qui n'ont pas d'initialiseur n'auront une valeur légale (par défaut) qu'une fois tous les initialiseurs de champ d'instance terminés. En raison de l'ordre d'initialisation, les constructeurs d'instance sont exécutés en dernier, c'est pourquoi l'instance n'est pas encore créée au moment où les initialiseurs sont exécutés. Par conséquent, le compilateur ne peut pas autoriser le référencement d'une propriété d'instance (ou d'un champ) avant que l'instance de classe ne soit entièrement construite. Cela est dû au fait que tout accès à une variable d'instance comme fait reminderréférence implicitement à l'instance ( this) pour indiquer au compilateur l'emplacement mémoire concret de l'instance à utiliser.

C'est également la raison pour laquelle thisn'est pas autorisé dans un initialiseur de champ d'instance.

Un initialiseur de variable pour un champ d'instance ne peut pas référencer l'instance en cours de création. Ainsi, c'est une erreur de compilation de référencer ceci dans un initialiseur de variable, car c'est une erreur de compilation pour un initialiseur de variable de référencer n'importe quel membre d'instance via un simple_name .

Les seuls membres de type dont l'initialisation est garantie avant l' exécution des initialiseurs de champ d'instance sont les initialiseurs de champ de classe (statique) et les constructeurs de classe (statique) et les méthodes de classe. Les membres statiques étant indépendants de l'instance, ils peuvent être référencés à tout moment:

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

C'est pourquoi les initialiseurs de champ d'instance sont uniquement autorisés à référencer un membre de classe (membre statique). Ces règles d'initialisation du compilateur assureront une instanciation de type déterministe.

Pour plus de détails, je recommande ce document: Microsoft Docs: déclarations de classe .

Cela signifie qu'un champ d'instance qui référence un autre membre d'instance pour initialiser sa valeur doit être initialisé à partir du constructeur d'instance ou que le membre référencé doit être déclaré static.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.