125 votes

Comment déclencher un événement lorsque la valeur d'une variable est modifiée ?

Je suis en train de créer une application en C# à l'aide de Visual Studio. Je veux créer du code pour que, lorsqu'une variable a la valeur 1, un certain morceau de code soit exécuté. Je sais que je peux utiliser une instruction if, mais le problème est que la valeur sera modifiée dans un processus asynchrone et que, techniquement, l'instruction if pourrait être ignorée avant que la valeur ait changé.

Est-il possible de créer un gestionnaire d'événement de sorte que lorsque la valeur de la variable change, un événement soit déclenché ? Si oui, comment puis-je le faire ?

Il est tout à fait possible que j'aie mal compris le fonctionnement d'une instruction if ! Toute aide serait la bienvenue.

1 votes

Pour être clair, observer le changement d'une variable n'est possible que pour une variable que vous possédez (ou qui est déjà liée à IObservable/INotifyPropertyChanged/Event). Vous ne pouvez pas observer le changement d'une variable système si elle n'a pas été conçue pour être observée.

159voto

Jonathan Wood Points 26443

Il me semble que vous voulez créer une propriété.

public int MyProperty
{
    get { return _myProperty; }
    set
    {
        _myProperty = value;
        if (_myProperty == 1)
        {
            // DO SOMETHING HERE
        }
    }
}

private int _myProperty;

Cela vous permet d'exécuter du code chaque fois que la valeur de la propriété change. Vous pourriez déclencher un événement ici, si vous le souhaitez.

77voto

Beatles1692 Points 2748

Vous pouvez utiliser un définisseur de propriété pour déclencher un événement lorsque la valeur d'un champ va changer.

Vous pouvez avoir votre propre délégué EventHandler ou vous pouvez utiliser le célèbre délégué System.EventHandler.

En général, il y a un modèle pour ça :

  1. Définissez un événement public avec un délégué de gestion d'événement (qui a un argument de type EventArgs).
  2. Définissez une méthode virtuelle protégée appelée OnXXXXX (OnMyPropertyValueChanged par exemple). Dans cette méthode, vous devez vérifier si le délégué du gestionnaire d'événement est nul et si ce n'est pas le cas, vous pouvez l'appeler (cela signifie qu'il y a une ou plusieurs méthodes attachées à la délégation d'événement).
  3. Appelez cette méthode protégée chaque fois que vous voulez informer les abonnés que quelque chose a changé.

Voici un exemple

private int _age;

//#1
public event System.EventHandler AgeChanged;

//#2
protected virtual void OnAgeChanged()
{ 
     if (AgeChanged != null) AgeChanged(this,EventArgs.Empty); 
}

public int Age
{
    get
    {
         return _age;
    }

    set
    {
         //#3
         _age=value;
         OnAgeChanged();
    }
 }

L'avantage de cette approche est que vous permettez à toute autre classe qui souhaite hériter de votre classe de modifier le comportement si nécessaire.

Si vous voulez attraper un événement dans un thread différent de celui où il est levé, vous devez faire attention à ne pas changer l'état des objets qui sont définis dans un autre thread, ce qui entraînerait la levée d'une exception inter-thread. Pour éviter cela, vous pouvez soit utiliser une méthode Invoke sur l'objet dont vous voulez changer l'état pour vous assurer que le changement a lieu dans le même thread que l'événement a été soulevé, soit, si vous avez affaire à un formulaire Windows, utiliser un BackgourndWorker pour faire les choses dans un thread parallèle de manière simple et efficace.

3 votes

L'une des meilleures explications de tout le Web. Je pense que je comprends enfin le Custom Event Handling. Merci pour ce billet.

46voto

Le cadre .NET fournit en fait une interface que vous pouvez utiliser pour notifier les abonnés lorsqu'une propriété a été modifiée : System.ComponentModel.INotifyPropertyChanged. Cette interface possède un événement PropertyChanged. Elle est généralement utilisée dans WPF pour la liaison, mais j'ai trouvé qu'elle était utile dans les couches métier comme moyen de normaliser la notification des changements de propriété.

En termes de sécurité des threads, je mettrais un verrou dans le setter afin d'éviter les conditions de course.

Voici mes pensées en code :) :

public class MyClass : INotifyPropertyChanged
{
    private object _lock;

    public int MyProperty
    {
        get
        {
            return _myProperty;
        }
        set
        {
            lock(_lock)
            {
                //The property changed event will get fired whenever
                //the value changes. The subscriber will do work if the value is
                //1. This way you can keep your business logic outside of the setter
                if(value != _myProperty)
                {
                    _myProperty = value;
                    NotifyPropertyChanged("MyProperty");
                }
            }
        }
    }

    private NotifyPropertyChanged(string propertyName)
    {
        //Raise PropertyChanged event
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

public class MySubscriber
{
    private MyClass _myClass;        

    void PropertyChangedInMyClass(object sender, PropertyChangedEventArgs e)
    {
        switch(e.PropertyName)
        {
            case "MyProperty":
                DoWorkOnMyProperty(_myClass.MyProperty);
                break;
        }
    }

    void DoWorkOnMyProperty(int newValue)
    {
        if(newValue == 1)
        {
             //DO WORK HERE
        }
    }
}

J'espère que cela vous sera utile :)

7 votes

+1 pour l'inclusion de la serrure que les autres réponses omettent.

1 votes

Quelle est l'utilité de l'objet _lock ?

2 votes

@LodeVlaeminck cela empêche de changer la valeur de la propriété pendant que l'événement est traité.

13voto

Russell Troywest Points 5400

Utilisez simplement une propriété

int  _theVariable;
public int TheVariable{
  get{return _theVariable;}
  set{
    _theVariable = value; 
    if ( _theVariable == 1){
      //Do stuff here.
    }
  }
}

5voto

Andrew Points 2027

Vous pouvez utiliser une classe générique :

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

        set
        {
            if ( _value != value )
            {
                _value = value;
                OnValueChanged();
            }
        }
    }

    protected virtual void OnValueChanged() => ValueChanged?.Invoke() ;
}

et sera en mesure de faire ce qui suit :

var i = new Wrapped<int>();

i.ValueChanged += () => { Console.WriteLine("changed!"); };

i.Value = 10;
i.Value = 10;
i.Value = 10;
i.Value = 10;

Console.ReadKey();

résultat :

changed!
changed!
changed!
changed!
changed!
changed!
changed!

2 votes

Personnellement, je n'aime pas la façon dont vous appelez OnValueChanged(); avant de modifier la valeur. Cela ressemble à une erreur de programmation qui va casser votre code.

3 votes

Personnellement, je vérifierais si la nouvelle valeur est différente de l'ancienne avant de déclencher l'événement de modification.

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