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

96

Je crée actuellement une application en C # à l'aide de Visual Studio. Je veux créer du code de sorte que lorsqu'une variable a une valeur de 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, donc techniquement, l'instruction if pourrait être ignorée avant que la valeur ait changé.

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

Il est tout à fait possible que j'aie mal compris comment fonctionne une instruction if! Toute aide serait très appréciée.

James Mundy
la source
1
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à IObservable / INotifyPropertyChanged / Event-related). Vous ne pouvez pas observer un changement de variable système s'il n'a pas été conçu pour être observé.
Cœur du

Réponses:

123

Il me semble que vous souhaitez 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 pouvez organiser un événement ici, si vous le souhaitez.

Jonathan Wood
la source
68

Vous pouvez utiliser un setter de propriétés pour déclencher un événement chaque fois que 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.

Il existe généralement un modèle pour cela:

  1. Définissez un événement public avec un délégué de gestionnaire d'événements (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énements est nul et sinon, vous pouvez l'appeler (cela signifie qu'il existe une ou plusieurs méthodes attachées à la délégation d'événements).
  3. Appelez cette méthode protégée chaque fois que vous souhaitez 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 laissez toutes les autres classes qui souhaitent hériter de votre classe modifier le comportement si nécessaire.

Si vous souhaitez intercepter un événement dans un thread différent qu'il est déclenché, vous devez faire attention à ne pas modifier l'état des objets définis dans un autre thread, ce qui entraînera la levée d'une exception de thread croisé. Pour éviter cela, vous pouvez utiliser une méthode Invoke sur l'objet dont vous souhaitez modifier son état pour vous assurer que la modification se produit dans le même thread que l'événement a été déclenché ou dans le cas où vous avez affaire à un Windows Form vous peut utiliser un BackgourndWorker pour faire des choses dans un thread parallèle de manière simple et agréable.

Beatles1692
la source
3
Une des meilleures explications sur tout le Web. Je pense que je comprends enfin la gestion des événements personnalisés. Merci pour ce post.
Au revoir le
43

Le framework .NET fournit en fait une interface que vous pouvez utiliser pour notifier les abonnés lorsqu'une propriété a changé: System.ComponentModel.INotifyPropertyChanged. Cette interface a un événement PropertyChanged. Il est généralement utilisé dans WPF pour la liaison, mais je l'ai trouvé utile dans les couches métier comme moyen de normaliser la notification de changement de propriété.

En termes de sécurité des threads, je mettrais un verrou sous le setter afin que vous ne rencontriez aucune condition de course.

Voici mes pensées dans le 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 :)

Daniel Sandberg
la source
6
+1 pour l'inclusion du verrou que les autres réponses omettent.
ctacke
1
A quoi sert l'objet _lock?
Lode Vlaeminck
2
@LodeVlaeminck cela empêche de changer la valeur de la propriété pendant le traitement de l'événement.
David Suarez
IMHO, c'est un endroit étrange pour une serrure. [Sauf si le verrou est également utilisé ailleurs, ce qui est une situation différente.] Si deux threads différents sont dans une condition de concurrence pour définir une propriété partagée, alors l'état "final" de la propriété n'est pas déterministe. Utilisez plutôt un modèle où un thread "possède" la propriété, et seulement ils la définissent. QUEL modèle dépend de la situation. (Si vraiment besoin de changer de propriétaire entre les threads, passez un bâton / jeton.) Si je rencontrais un besoin de verrou ici, j'examinerais attentivement la conception globale. OTOH, un verrou ici est inoffensif.
ToolmakerSteve
13

utilisez simplement une propriété

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

vous pouvez utiliser la classe générique:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

et pourra effectuer les opérations suivantes:

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!
Andrew
la source