Come attivare un evento quando il valore di una variabile viene modificato?


96

Attualmente sto creando un'applicazione in C # utilizzando Visual Studio. Voglio creare del codice in modo che quando una variabile ha un valore di 1, viene eseguita una certa parte di codice. So che posso usare un'istruzione if ma il problema è che il valore verrà modificato in un processo asincrono, quindi tecnicamente l'istruzione if potrebbe essere ignorata prima che il valore sia cambiato.

È possibile creare un gestore di eventi in modo che quando il valore della variabile cambia viene attivato un evento? Se è così, come posso farlo?

È del tutto possibile che avrei potuto fraintendere come funziona un'istruzione if! Qualsiasi aiuto sarebbe molto apprezzato.


1
Giusto per essere chiari, l'osservazione del cambiamento di una variabile è possibile solo per una variabile di tua proprietà (o che è già IObservable / INotifyPropertyChanged / Event-related). Non è possibile osservare una modifica della variabile di sistema se non è stata progettata per essere osservata.
Cœur

Risposte:


123

Mi sembra che tu voglia creare una proprietà.

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

private int _myProperty;

Ciò consente di eseguire del codice ogni volta che il valore della proprietà cambia. Potresti organizzare un evento qui, se lo desideri.


68

È possibile utilizzare un setter di proprietà per generare un evento ogni volta che il valore di un campo cambierà.

Puoi avere il tuo delegato EventHandler oppure puoi usare il famoso delegato System.EventHandler.

Di solito c'è uno schema per questo:

  1. Definire un evento pubblico con un delegato del gestore eventi (con un argomento di tipo EventArgs).
  2. Definisci un metodo virtuale protetto chiamato OnXXXXX (OnMyPropertyValueChanged per esempio). In questo metodo dovresti controllare se il delegato del gestore eventi è null e in caso contrario puoi chiamarlo (significa che ci sono uno o più metodi collegati alla delega dell'evento).
  3. Chiama questo metodo protetto ogni volta che desideri informare gli abbonati che qualcosa è cambiato.

Ecco un esempio

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

Il vantaggio di questo approccio è che consenti a qualsiasi altra classe che desidera ereditare dalla tua classe di modificare il comportamento, se necessario.

Se si desidera catturare un evento in un thread diverso che viene generato, è necessario fare attenzione a non modificare lo stato degli oggetti definiti in un altro thread che causerà la generazione di un'eccezione di cross thread. Per evitare ciò è possibile utilizzare un metodo Invoke sull'oggetto di cui si desidera modificare lo stato per assicurarsi che la modifica avvenga nello stesso thread in cui è stato generato l'evento o nel caso in cui si abbia a che fare con un Windows Form che si può usare un BackgourndWorker per fare le cose in un thread parallelo piacevole e facile.


3
Una delle migliori spiegazioni su tutto il Web. Penso di aver finalmente capito la gestione degli eventi personalizzati. Grato per questo post.
Arrivederci

43

Il framework .NET fornisce effettivamente un'interfaccia che è possibile utilizzare per notificare agli abbonati quando una proprietà è cambiata: System.ComponentModel.INotifyPropertyChanged. Questa interfaccia ha un evento PropertyChanged. Viene solitamente utilizzato in WPF per l'associazione, ma l'ho trovato utile nei livelli aziendali come un modo per standardizzare la notifica di modifica delle proprietà.

In termini di thread safety, metterei un lucchetto nel setter in modo da non incorrere in condizioni di gara.

Ecco i miei pensieri in codice :):

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
        }
    }
}

Spero che questo sia utile :)


6
+1 per l'inserimento del lucchetto che le altre risposte omettono.
ctacke

1
Qual è l'uso dell'oggetto _lock?
Lode Vlaeminck

2
@LodeVlaeminck impedisce di modificare il valore della proprietà durante l'elaborazione dell'evento.
David Suarez

IMHO, questo è un posto strano per una serratura. [A meno che il blocco non venga utilizzato anche altrove, il che è una situazione diversa.] Se due thread diversi sono in una condizione di competizione per impostare una proprietà condivisa, lo stato "finale" della proprietà non è deterministico. Utilizzare invece un modello in cui un thread "possiede" la proprietà e solo loro lo impostano. QUALE modello dipende dalla situazione. (Se è davvero necessario cambiare la proprietà tra i thread, passare un testimone / gettone.) Se dovessi riscontrare la necessità di un blocco qui, esaminerei attentamente il design generale. OTOH, una serratura qui è innocua.
ToolmakerSteve

13

usa solo una proprietà

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

0

puoi usare la classe generica:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

e sarà in grado di fare quanto segue:

var i = new Wrapped<int>();

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

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

Console.ReadKey();

risultato:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
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.