변수 값이 변경 될 때 이벤트를 트리거하는 방법은 무엇입니까?


96

현재 Visual Studio를 사용하여 C #으로 응용 프로그램을 만들고 있습니다. 변수의 값이 1 일 때 특정 코드가 수행되도록 코드를 만들고 싶습니다. if 문을 사용할 수 있지만 문제는 비동기 프로세스에서 값이 변경되므로 값이 변경되기 전에 기술적으로 if 문을 무시할 수 있다는 것입니다.

변수 값이 변경 될 때 이벤트가 트리거되도록 이벤트 핸들러를 만들 수 있습니까? 그렇다면 어떻게해야합니까?

if 문이 어떻게 작동하는지 오해했을 가능성이 있습니다! 어떤 도움이라도 대단히 감사하겠습니다.


1
명확하게 말하면, 변수의 변경 사항을 관찰하는 것은 소유 한 변수 (또는 이미 IObservable / INotifyPropertyChanged / Event 관련)에 대해서만 가능합니다. 관찰하도록 설계되지 않은 경우 시스템 변수 변경을 관찰 할 수 없습니다.
Cœur

답변:


124

당신이 속성을 만들고 싶어하는 것 같습니다.

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

private int _myProperty;

이렇게하면 속성 값이 변경 될 때마다 일부 코드를 실행할 수 있습니다. 원한다면 여기서 이벤트를 제기 할 수 있습니다.


68

필드 값이 변경 될 때마다 속성 설정기를 사용하여 이벤트를 발생시킬 수 있습니다.

고유 한 EventHandler 대리자를 가질 수도 있고 유명한 System.EventHandler 대리자를 사용할 수도 있습니다.

일반적으로 이에 대한 패턴이 있습니다.

  1. EventArgs 유형의 인수가있는 이벤트 처리기 대리자를 사용하여 공용 이벤트를 정의합니다.
  2. OnXXXXX (예 : OnMyPropertyValueChanged)라는 보호 된 가상 메서드를 정의합니다. 이 메서드에서 이벤트 처리기 대리자가 null인지, 그렇지 않은 경우이를 호출 할 수 있는지 확인해야합니다 (이벤트 위임에 연결된 메서드가 하나 이상 있음을 의미 함).
  3. 구독자에게 변경 사항을 알리고 싶을 때마다이 보호 된 메서드를 호출하십시오.

여기에 예가 있습니다.

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

이 접근 방식의 장점은 클래스에서 상속하려는 다른 클래스가 필요한 경우 동작을 변경하도록 허용한다는 것입니다.

발생하는 다른 스레드에서 이벤트를 포착하려면 다른 스레드에서 정의 된 객체의 상태를 변경하지 않도록주의해야합니다. 이로 인해 교차 스레드 예외가 발생합니다. 이를 방지하려면 상태를 변경하려는 개체에서 Invoke 메서드를 사용하여 이벤트가 발생한 동일한 스레드에서 변경이 발생하는지 확인하거나 Windows Form을 처리하는 경우 BackgourndWorker를 사용하여 병렬 스레드에서 쉽고 편리하게 작업 할 수 있습니다.


3
전체 웹에 대한 최고의 설명 중 하나입니다. 마침내 Custom Event Handling을 이해하고 있다고 생각합니다. 이 게시물에 감사드립니다.
Goodbye

44

.NET 프레임 워크는 속성이 변경된 경우 구독자에게 알리는 데 사용할 수있는 인터페이스 인 System.ComponentModel.INotifyPropertyChanged를 실제로 제공합니다. 이 인터페이스에는 PropertyChanged 이벤트가 하나 있습니다. 일반적으로 바인딩을 위해 WPF에서 사용되지만 속성 변경 알림을 표준화하는 방법으로 비즈니스 계층에서 유용하다는 것을 알았습니다.

스레드 안전성 측면에서 나는 당신이 어떤 경쟁 조건에 부딪치지 않도록 setter에 잠금을 둘 것입니다.

코드에 대한 내 생각은 다음과 같습니다.

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

이것이 도움이 되었기를 바랍니다 :)


6
+1은 다른 답변이 생략 한 잠금을 포함합니다.
ctacke 2011-04-30

1
_lock 객체의 용도는 무엇입니까?
Lode Vlaeminck 2015 년

2
@LodeVlaeminck 이벤트가 처리되는 동안 속성 값을 변경하지 못하도록합니다.
David Suarez 2016

IMHO, 이것은 자물쇠에 대한 이상한 장소입니다. [잠금이 다른 곳에서도 사용되지 않는 한, 이는 다른 상황입니다.] 두 개의 다른 스레드가 공유 속성을 설정하기위한 경쟁 조건에있는 경우 속성의 "최종"상태는 결정적이지 않습니다. 대신 한 스레드가 속성을 "소유"하고 설정하는 패턴을 사용합니다. 어떤 패턴은 상황에 따라 다릅니다. (실제로 스레드 간의 소유권을 변경해야하는 경우 배턴 / 토큰을 전달합니다.) 여기서 잠금이 필요하면 전체 디자인을주의 깊게 검토합니다. OTOH, 여기 자물쇠는 무해합니다.
ToolmakerSteve

13

그냥 속성을 사용

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

0

일반 클래스를 사용할 수 있습니다.

class Wrapped<T>  {
    private T _value;

    public Action ValueChanged;

    public T Value
    {
        get => _value;

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

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

다음을 수행 할 수 있습니다.

var i = new Wrapped<int>();

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

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

Console.ReadKey();

결과:

changed!
changed!
changed!
changed!
changed!
changed!
changed!
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.