이벤트와 델리게이트 간의 관계가 복합 패턴을 채택한다고 말할 수 있습니까?

Nov 18 2020

이벤트는를 사용하여 정의 된 많은 핸들러를 포함 할 수 있습니다. delegate현재 제가 이해하는 것은 델리게이트가 함수 포인터의 추상화 일 뿐이라는 것입니다. 때문에 event하는과 관련된 어떤 delegate아이디어가되도록 복합 패턴 취급을 복합 오브젝트 단말 객체와 같은 타입 / 추가 그것에 많은 대리자를 제거하고 :

composite.onTriggered();
// Internally:
// foreach(handler in composite)
// {
//     handler.onTriggered();
// }

에서 관리하는 모든 핸들러를 차례로 호출합니다 composite.

하지만 public event EventHandler ThresholdReached복합을 정의하지 않는 것 같습니다. 아래 코드의 내 의견을 참조하십시오.

class Counter
{
    public event EventHandler ThresholdReached;

    protected virtual void OnThresholdReached(EventArgs e)
    {
        EventHandler handler = ThresholdReached; // So what's the point of this line?
        handler?.Invoke(this, e);
        // Why not just:
     // ThresholdReached?.Invoke(this, e);
    } 

    // provide remaining implementation for the class
}

추상적 인 수준의 아이디어가 맞습니까? 정정을 제공 할 수 없다면?

답변

3 MiguelGamboa Nov 20 2020 at 11:18

귀하의 질문에 직접 대답하면 다음과 같습니다. 아니요, 이벤트와 복합 패턴을 채택한 대리자 간에는 관계가 없습니다 . 대표 디자인 예 , 복합 패턴을 따릅니다. 이벤트가 아닙니다 . (또한, 델리게이트를 활용하기 위해 이벤트가 필요하지 않습니다. (아래 참조 DelegateBased)) ( 끝 부분 에 " 그래서이 라인의 요점은 무엇입니까? "에 대한 귀하의 의견에 사이드 노트로 답변하겠습니다. )

그럼에도 불구하고 Delegate 유형 자체는“ 복합 패턴 은 동일한 유형의 개체 의 단일 인스턴스 와 동일한 방식으로 처리되는 개체 그룹을 설명 합니다. ”.

차례로 @ Flydog57 및 @ mark-seemann이 이미 언급했듯이 .NET 이벤트 모델은 관찰자 패턴을 따릅니다 .

이벤트 및 대리자 사이의 관계는 안부 이벤트 선언 이 필요할 수 있습니다 대리자 형식을합니다 ( TypeSpec이 섹션에 명시된대로를) II.18 정의 이벤트 의 VI에 ECMA-335 (CLI) 파티션 I (표준) :

일반적인 사용에서 TypeSpec (있는 경우)은 서명이 이벤트의 fire 메서드에 전달 된 인수와 일치하는 대리자를 식별합니다.

그것은 명확하게하려면 다음 두 가지 동등한 예 확인 EventBased용도의 대리자 필드없이 이벤트DelegateBased사용 이벤트없이 위임 필드를 . 내가 명백하게 delegate field 또는 delegate type 이라고 말하는 것을 주목하십시오 . 그들은 동일하지 않습니다. 두 예제 모두이 예제 에서 다음과 같이 선언 된 대리자 형식 이 필요합니다 .

delegate void Observer();

다음과 같이 두 가지 예를 모두 실행할 수 있습니다.

var subject = new DelegateBased(); // replace it with: var subject = new EventBased();
Observer foo = () => Console.Write("Foo");
Observer bar = () => Console.Write("Bar");
subject.RegisterObserver(foo); // subject.Caller += foo;
subject.RegisterObserver(bar); // subject.Caller += bar;
subject.Notify(); // prints: FooBar
Console.WriteLine();
subject.UnregisterObserver(foo); // subject.Caller -= foo;
subject.Notify(); // prints: Bar

다음 으로 Wikipedia 의 Observer Pattern 예제에 따라 이름을 사용 하는 EventBased및 의 두 구현DelegateBased

class EventBased {
  private List<Observer> observers = new List<Observer>();
  public event Observer Caller {
    add { RegisterObserver(value); }
    remove { UnregisterObserver(value); }
  }
  public void Notify() { foreach (var caller in observers) caller(); }
    
  public void RegisterObserver(Observer val) {  observers.Add(val); }
    
  public void UnregisterObserver(Observer val) { observers.Remove(val); }
}
class DelegateBased {
  private Observer observers; // delegate field without events
    
  public void Notify() { observers(); }

  public void RegisterObserver(Observer val) { 
    observers = (Observer) Delegate.Combine(observers, val); // <=> observers += val
  }
  public void UnregisterObserver(Observer val) {
    observers = (Observer) Delegate.Remove(observers, val); // <=> observers -= val
  }
}

다음에 대한 귀하의 의견과 관련하여 :

EventHandler handler = ThresholdReached; // So what's the point of this line?
handler?.Invoke(this, e);

Jeffrey Richter가 "C #을 통한 Clr"의 걸작 인 Chapter 11-Events at " Raising an Event in a Thread-Safe Way " 에서 명확하게 식별 한 이유는 다음 NewMailThresholdReached같습니다.

의 문제 OnNewMail방법은 스레드가 볼 수 있다는 것입니다 NewMailnull는 아니고, 다음, 바로 호출하기 전에 NewMail, 다른 스레드가 체인 결정에서 대리자를 제거 할 수 NewMail nullA의 결과로 NullReferenceException발생된다.

2 MarkSeemann Nov 19 2020 at 06:43

Flydog57이 지적했듯이 .NET 이벤트 모델은 기본적으로 언어에 내장 된 Observer 패턴 IEnumerable이며 foreachIterator 패턴 과 마찬가지로 구현합니다.

그러나 Gang of Four 책의 패턴은 다양한 추상화 수준에 있습니다. 나는 이것이 1994 년에 누구에게도 분명하다고 확신하지 못하지만, 수십 년 동안 사용함에 따라 이러한 패턴 중 일부가 다른 패턴보다 더 일반적이라는 것이 점점 더 명확 해지고 있습니다. 그러한 패턴 중 하나는 장식 자 패턴을 퇴화 전문화로 볼 수있는 어댑터 패턴입니다.

또 다른 패턴은 Composite입니다. 책의 다른 패턴 중 일부는 Composite의 전문화로 볼 수 있습니다. Observer뿐만 아니라 Command and State (적어도 책에 설명 된대로); 아마도 다른.

직감이 맞다고 생각합니다. 이벤트는 관찰자 패턴 이후에 가장 구체적으로 패턴 화되지만이를 합성물로 생각할 수도 있습니다. .NET 이벤트 대신 Rx (Reactive Extensions) 및 IObserver인터페이스를 고려 하면 더 명확해질 수 있습니다 . IIRC, Rx는 .NET 이벤트와 자체 모델 간의 변환을 정의합니다.

보다 일반적으로 모노 이드를 생성하는 모든 API는 Composite로 모델링 할 수 있습니다 . 이벤트는 데이터를 반환하지 않으므로 ( void메서드 서명이 있음) 모노 이드를 형성합니다. 따라서 복합 디자인 패턴의 인스턴스로 볼 수도 있습니다.