125 votes

Un initialisateur de champ ne peut pas faire référence à un champ, une méthode ou une propriété non statique.

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

    }
}

Utilisation de 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 se produit-il et comment le réparer ?

172voto

Oded Points 271275

Cette ligne :

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

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

Au lieu de cela, utilisez simplement :

private dynamic defaultReminder = TimeSpan.FromMinutes(15);

Vous pouvez également définir 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 de compilation sur MSDN -. Erreur de compilation CS0236 .

3 votes

Java est plus "indulgent" pour ce type de constructions. Je ne sais pas si c'est une bonne chose. stackoverflow.com/questions/1494735/

45 votes

Non, le compilateur ne peut pas réorganiser les initialisateurs. La spécification du langage C# indique ce qui suit dans la section "10.5.5.2 Initialisation des champs d'instance" : Les initialisateurs de variables sont exécutés dans l'ordre textuel dans lequel ils apparaissent dans la déclaration de la classe. Ceci est même répété dans "10.11.2 Instance variable initializers" où l'on dit : Les initialisateurs de variables sont exécutés dans l'ordre textuel dans lequel ils apparaissent dans la déclaration de la classe. Votre explication est donc erronée. L'ordre est fixé. La raison pour laquelle il n'est pas autorisé est que les concepteurs de C# l'ont voulu ainsi.

1 votes

(Uniquement dans le cas d'un partial class avec des "parties" dans plusieurs fichiers, l'ordre des initialisateurs de champs n'est pas clair, mais il en va de même pour les static ).

29voto

Daniel Hilgarth Points 90722

Vous devez placer 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 en est que vous ne pouvez pas utiliser une variable d'instance pour en initialiser une autre à l'aide d'un initialisateur de champ.

10voto

Que vous pouvez utiliser comme ceci

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

11 votes

Bienvenue sur Stack Overflow ! Bien que cet extrait de code puisse résoudre la question, l'inclusion d'un explication contribue réellement à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question pour les lecteurs à venir, et que ces derniers pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas encombrer votre code de commentaires explicatifs, car cela réduit la lisibilité du code et des explications !

5 votes

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

10 votes

Soyez prudent dans l'utilisation de cette technique, car l'utilisation de => ne fixe pas la valeur réelle, mais exécute le code à chaque fois que defaultReminder est accessible. Cela peut ne pas être voulu et avoir un impact négatif sur les performances, ou générer une pression indésirable pour le GC, etc.

8voto

BionicCode Points 4677

private dynamic defaultReminder = reminder.TimeSpanText[TimeSpan.FromMinutes(15)]; est un initialisateur de champ et s'exécute en premier (avant que tout champ sans initialisateur ne prenne sa valeur par défaut et avant que le constructeur d'instance invoqué ne soit exécuté). Les champs d'instance qui n'ont pas d'initialisateur n'auront une valeur légale (par défaut) qu'après l'exécution de tous les initialisateurs de champs d'instance. 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 initialisateurs sont exécutés. Le compilateur ne peut donc pas permettre qu'une propriété (ou un champ) d'instance soit référencée avant que l'instance de la classe ne soit entièrement construite. En effet, tout accès à une variable d'instance telle que reminder fait implicitement référence à l'instance ( this ) pour indiquer au compilateur l'emplacement mémoire concret de l'instance à utiliser.

C'est aussi la raison pour laquelle this n'est pas autorisé dans un initialisateur de champ d'instance.

Un initialisateur de variable pour un champ d'instance ne peut pas faire référence à l'élément instance en cours de création. Ainsi, c'est une erreur de compilation que de faire référence à dans un initialisateur de variable, comme c'est une erreur de compilation pour un initialisateur de de référencer tout membre d'une instance par l'intermédiaire d'un champ de type simple_name .

Les seuls membres du type qui sont garantis d'être initialisés avant Les initialisateurs de champs d'instance sont exécutés sont les initialisateurs de champs de classe (statiques) et les constructeurs de classe (statiques) et les méthodes de classe. Les membres statiques étant indépendants des instances, 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 initialisateurs de champs d'instance ne peuvent faire référence qu'à un membre de la classe (membre statique). Ces règles d'initialisation du compilateur assureront une instanciation de type déterministe.

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

Cela signifie qu'un champ d'instance qui fait référence à un autre membre d'instance pour initialiser sa valeur, doit être initialisé depuis le constructeur de l'instance ou le membre référencé doit être déclaré static .

1voto

ashkan nasirzadeh Points 305

Je suis totalement surpris de la réponse acceptée ici par la communauté, qui est totalement erronée, comme le dit la réponse acceptée :

Parce que le compilateur peut réarranger ces

comme le dit "Jeppe Stig Nielsen" dans un commentaire de la réponse acceptée :

La spécification du langage C# stipule que Les initialisateurs de variables sont sont exécutés dans l'ordre textuel dans lequel ils apparaissent dans la de la classe

ce qui ne veut pas dire que C# est un langage d'interprétation, mais cela ne veut pas non plus dire que C# peut réarranger des lignes de code !

Pourquoi cette erreur se produit-elle ? Supposons le code ci-dessous :

public class MyClass
{
    public int i = 5;
    public int j = i;  // CS0236

    public MyClass()
    {
        // ...
    }
}

Laissez-moi vous montrer comment le compilateur C# voit ce code :

public class MyClass
{
    public int i = 5;
    public int j = this.i;  // CS0236

    public MyClass()
    {
        // ...
    }
}

Le mot-clé "this" qui apparaît dans public int j = this.i; référence MyClass mais MyClass n'existe pas encore, pourquoi ? parce que les classes commencent à exister lorsque leurs constructeurs terminent leurs exécutions et en C# les constructeurs logiques sont exécutés en dernier, ce qui signifie qu'ils seront exécutés après les initialisateurs des champs/variables de l'instance, donc en public int j = this.i; l'expression, this ne fait référence à rien !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X