Wie kann ein Ereignis ausgelöst werden, wenn der Wert einer Variablen geändert wird?

96

Ich erstelle derzeit eine Anwendung in C # mit Visual Studio. Ich möchte einen Code erstellen, damit, wenn eine Variable den Wert 1 hat, ein bestimmter Code ausgeführt wird. Ich weiß, dass ich eine if-Anweisung verwenden kann, aber das Problem ist, dass der Wert in einem asynchronen Prozess geändert wird, sodass die if-Anweisung technisch ignoriert werden kann, bevor sich der Wert geändert hat.

Ist es möglich, einen Ereignishandler zu erstellen, damit bei einer Änderung des Variablenwerts ein Ereignis ausgelöst wird? Wenn ja, wie kann ich das machen?

Es ist durchaus möglich, dass ich falsch verstanden habe, wie eine if-Anweisung funktioniert! Jede Hilfe wäre sehr dankbar.

James Mundy
quelle
1
Um ganz klar zu sein, ist die Beobachtung der Änderung einer Variablen nur für eine Variable möglich, die Sie besitzen (oder die bereits IObservable / INotifyPropertyChanged / Event-related ist). Sie können eine Änderung der Systemvariablen nicht beobachten, wenn sie nicht zur Beobachtung ausgelegt ist.
Cœur

Antworten:

123

Mir scheint, Sie möchten eine Immobilie erstellen.

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

private int _myProperty;

Auf diese Weise können Sie jederzeit Code ausführen, wenn sich der Eigenschaftswert ändert. Sie könnten hier eine Veranstaltung veranstalten, wenn Sie möchten.

Jonathan Wood
quelle
67

Sie können einen Eigenschaftssetter verwenden, um ein Ereignis auszulösen, wenn sich der Wert eines Felds ändert.

Sie können Ihren eigenen EventHandler-Delegaten haben oder den berühmten System.EventHandler-Delegaten verwenden.

Normalerweise gibt es dafür ein Muster:

  1. Definieren Sie ein öffentliches Ereignis mit einem Event-Handler-Delegaten (der ein Argument vom Typ EventArgs hat).
  2. Definieren Sie eine geschützte virtuelle Methode namens OnXXXXX (z. B. OnMyPropertyValueChanged). Bei dieser Methode sollten Sie überprüfen, ob der Event-Handler-Delegat null ist, und wenn nicht, können Sie ihn aufrufen (dies bedeutet, dass der Event-Delegation eine oder mehrere Methoden zugeordnet sind).
  3. Rufen Sie diese geschützte Methode auf, wenn Sie Abonnenten benachrichtigen möchten, dass sich etwas geändert hat.

Hier ist ein Beispiel

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

Der Vorteil dieses Ansatzes besteht darin, dass Sie alle anderen Klassen, die von Ihrer Klasse erben möchten, bei Bedarf das Verhalten ändern lassen.

Wenn Sie ein Ereignis in einem anderen Thread abfangen möchten, das ausgelöst wird, müssen Sie darauf achten, den Status von Objekten, die in einem anderen Thread definiert sind, nicht zu ändern, da dadurch eine Thread-übergreifende Ausnahme ausgelöst wird. Um dies zu vermeiden, können Sie entweder eine Invoke-Methode für das Objekt verwenden, dessen Status Sie ändern möchten, um sicherzustellen, dass die Änderung in demselben Thread erfolgt, in dem das Ereignis ausgelöst wurde, oder für den Fall, dass Sie mit einem Windows Form arbeiten kann einen BackgourndWorker verwenden, um Dinge in einem parallelen Thread nett und einfach zu erledigen.

Beatles1692
quelle
3
Eine der besten Erklärungen im gesamten Web. Ich glaube, ich verstehe endlich die benutzerdefinierte Ereignisbehandlung. Dankbar für diesen Beitrag.
Auf Wiedersehen
43

Das .NET Framework bietet tatsächlich eine Schnittstelle, über die Sie Abonnenten benachrichtigen können, wenn sich eine Eigenschaft geändert hat: System.ComponentModel.INotifyPropertyChanged. Diese Schnittstelle hat ein Ereignis PropertyChanged. Es wird normalerweise in WPF zum Binden verwendet, aber ich habe es in Geschäftsschichten als nützlich erachtet, um die Benachrichtigung über Eigenschaftsänderungen zu standardisieren.

In Bezug auf die Gewindesicherheit würde ich im Setter ein Schloss unterbringen, damit Sie keinen Rennbedingungen ausgesetzt sind.

Hier sind meine Gedanken im 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
        }
    }
}

Hoffe das ist hilfreich :)

Daniel Sandberg
quelle
6
+1 für die Aufnahme der Sperre, die die anderen Antworten weglassen.
Ctacke
1
Was nützt das Objekt _lock?
Lode Vlaeminck
2
@LodeVlaeminck verhindert, dass der Wert der Eigenschaft geändert wird, während das Ereignis verarbeitet wird.
David Suarez
IMHO, das ist ein seltsamer Ort für ein Schloss. [Es sei denn, die Sperre wird auch an anderer Stelle verwendet. Dies ist eine andere Situation.] Wenn sich zwei verschiedene Threads in einer Race-Bedingung befinden, um eine gemeinsam genutzte Eigenschaft festzulegen, ist der "endgültige" Status der Eigenschaft nicht deterministisch. Verwenden Sie stattdessen ein Muster, bei dem ein Thread die Eigenschaft "besitzt" und nur sie festlegen. Welches Muster von der Situation abhängt. (Wenn Sie den Besitz zwischen den Threads wirklich ändern müssen, übergeben Sie einen Staffelstab / Token.) Wenn ich hier eine Sperre benötigte, würde ich das Gesamtdesign sorgfältig prüfen. OTOH, ein Schloss hier ist harmlos.
ToolmakerSteve
13

Verwenden Sie einfach eine Eigenschaft

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

Sie können die generische Klasse verwenden:

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

und wird in der Lage sein, Folgendes zu tun:

var i = new Wrapped<int>();

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

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

Console.ReadKey();

Ergebnis:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
Andrew
quelle