.NET의 이벤트 서명 — 강력한 유형의 '보낸 사람'사용?


106

나는 내가 제안하는 것이 .NET 지침을 따르지 않는다는 것을 완전히 알고 있으며, 따라서 이러한 이유만으로는 아마도 좋지 않은 생각 일 것입니다. 그러나 두 가지 가능한 관점에서 이것을 고려하고 싶습니다.

(1) 100 % 내부 용으로 개발 작업에 사용하는 것을 고려해야합니다.

(2) 이것은 프레임 워크 디자이너가 변경 또는 업데이트를 고려할 수있는 개념입니까?

현재 .NET 디자인 패턴 인 '객체'로 입력하는 대신 강력한 유형의 '보낸 사람'을 활용하는 이벤트 서명을 사용할 생각입니다. 즉, 다음과 같은 표준 이벤트 서명을 사용하는 대신 :

class Publisher
{
    public event EventHandler<PublisherEventArgs> SomeEvent;
}

다음과 같이 강력한 형식의 '보낸 사람'매개 변수를 사용하는 이벤트 서명 사용을 고려하고 있습니다.

먼저 "StrongTypedEventHandler"를 정의합니다.

[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
    TSender sender,
    TEventArgs e
)
where TEventArgs : EventArgs;

이것은 Action <TSender, TEventArgs>와 크게 다르지 않지만를 사용 StrongTypedEventHandler하여 TEventArgs가 System.EventArgs.

다음으로, 예를 들어 다음과 같이 게시 클래스에서 StrongTypedEventHandler를 사용할 수 있습니다.

class Publisher
{
    public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;

    protected void OnSomeEvent()
    {
        if (SomeEvent != null)
        {
            SomeEvent(this, new PublisherEventArgs(...));
        }
    }
}

위의 배열을 통해 구독자는 캐스팅이 필요하지 않은 강력한 유형의 이벤트 처리기를 사용할 수 있습니다.

class Subscriber
{
    void SomeEventHandler(Publisher sender, PublisherEventArgs e)
    {           
        if (sender.Name == "John Smith")
        {
            // ...
        }
    }
}

나는 이것이 표준 .NET 이벤트 처리 패턴으로 깨진다는 것을 완전히 알고 있습니다. 그러나 반 변성은 구독자가 원하는 경우 기존 이벤트 처리 서명을 사용할 수 있도록합니다.

class Subscriber
{
    void SomeEventHandler(object sender, PublisherEventArgs e)
    {           
        if (((Publisher)sender).Name == "John Smith")
        {
            // ...
        }
    }
}

즉, 이벤트 처리기가 서로 다른 (또는 알 수없는) 개체 유형의 이벤트를 구독해야하는 경우 처리기는 잠재적 인 발신자 개체의 전체 범위를 처리하기 위해 'sender'매개 변수를 'object'로 입력 할 수 있습니다.

관습을 깨뜨리는 것 외에 (저를 가볍게 받아들이지 않는 것입니다.) 저는 이것에 대한 어떤 단점도 생각할 수 없습니다.

여기에 몇 가지 CLS 규정 준수 문제가있을 수 있습니다. 이것은 Visual Basic .NET 2008에서 100 % 잘 실행되지만 (필자가 테스트 한) 이전 버전의 Visual Basic .NET에서 2005 년까지의 이전 버전에는 대리자 공분산 및 반공 변성이 없다고 생각합니다. [편집 : 나는 이것을 테스트 한 이후로 확인되었습니다 : VB.NET 2005 이하에서는 이것을 처리 할 수 ​​없지만 VB.NET 2008은 100 % 괜찮습니다. 아래의 "편집 # 2"를 참조하십시오.] 이 문제가있는 다른 .NET 언어가있을 수 있습니다. 확실하지 않습니다.

그러나 나는 C # 또는 Visual Basic .NET 이외의 다른 언어로 개발하는 것을 보지 않으며 .NET Framework 3.0 이상을 위해 C # 및 VB.NET으로 제한하는 것을 신경 쓰지 않습니다. (솔직히이 시점에서 2.0으로 돌아가는 것은 상상할 수 없습니다.)

다른 사람이 이것에 대한 문제를 생각할 수 있습니까? 아니면 이것은 단순히 관습을 너무 많이 깨뜨려 사람들의 배를 돌리게하는 것일까 요?

내가 찾은 관련 링크는 다음과 같습니다.

(1) 이벤트 디자인 가이드 라인 [MSDN 3.5]

(2) C # 단순 이벤트 발생- "보낸 사람"대 사용자 지정 EventArgs 사용 [StackOverflow 2009]

(3) .net의 이벤트 시그니처 패턴 [StackOverflow 2008]

나는 이것에 대한 모든 사람의 의견에 관심이 있습니다 ...

미리 감사드립니다.

마이크

편집 # 1 : 이것은 Tommy Carlier의 게시물 에 대한 응답입니다 .

다음은 강력한 형식의 이벤트 처리기와 '객체 전송자'매개 변수를 사용하는 현재 표준 이벤트 처리기가이 접근 방식과 공존 할 수 있음을 보여주는 전체 작동 예제입니다. 코드를 복사하여 붙여넣고 실행할 수 있습니다.

namespace csScrap.GenericEventHandling
{
    class PublisherEventArgs : EventArgs
    {
        // ...
    }

    [SerializableAttribute]
    public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
        TSender sender,
        TEventArgs e
    )
    where TEventArgs : EventArgs;

    class Publisher
    {
        public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;

        public void OnSomeEvent()
        {
            if (SomeEvent != null)
            {
                SomeEvent(this, new PublisherEventArgs());
            }
        }
    }

    class StrongTypedSubscriber
    {
        public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
        {
            MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
        }
    }

    class TraditionalSubscriber
    {
        public void SomeEventHandler(object sender, PublisherEventArgs e)
        {
            MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
        }
    }

    class Tester
    {
        public static void Main()
        {
            Publisher publisher = new Publisher();

            StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
            TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();

            publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
            publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;

            publisher.OnSomeEvent();
        }
    }
}

편집 # 2 : 이것은 공분산 및 반공 변성에 관한 Andrew Hare의 진술 과 여기에 적용되는 방법 에 대한 응답 입니다. C # 언어의 델리게이트는 오랫동안 공분산과 반공 변성을 가지고있어 "내재적"이라고 느껴지지만 그렇지 않습니다. CLR에서 활성화 된 것일 수도 있지만, Visual Basic .NET은 .NET Framework 3.0 (VB.NET 2008)이 출시 될 때까지 대리자에 대한 공분산 및 반공 변성 기능을 얻지 못했습니다. 결과적으로 Visual Basic.NET for .NET 2.0 이하에서는이 접근 방식을 사용할 수 없습니다.

예를 들어, 위의 예는 다음과 같이 VB.NET으로 변환 될 수 있습니다.

Namespace GenericEventHandling
    Class PublisherEventArgs
        Inherits EventArgs
        ' ...
        ' ...
    End Class

    <SerializableAttribute()> _
    Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
        (ByVal sender As TSender, ByVal e As TEventArgs)

    Class Publisher
        Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)

        Public Sub OnSomeEvent()
            RaiseEvent SomeEvent(Me, New PublisherEventArgs)
        End Sub
    End Class

    Class StrongTypedSubscriber
        Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
            MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
        End Sub
    End Class

    Class TraditionalSubscriber
        Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
            MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
        End Sub
    End Class

    Class Tester
        Public Shared Sub Main()
            Dim publisher As Publisher = New Publisher

            Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
            Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber

            AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
            AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler

            publisher.OnSomeEvent()
        End Sub
    End Class
End Namespace

VB.NET 2008은 100 % 잘 실행할 수 있습니다. 하지만 지금은 VB.NET 2005에서 테스트를 해봤습니다. 확실히하기 위해 다음과 같이 컴파일되지 않습니다.

'Public Sub SomeEventHandler (sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)'메서드에 'Delegate Sub StrongTypedEventHandler (Of TSender, TEventArgs As System.EventArgs) (sender As Publisher, e As PublisherEventArgs)'대리자와 동일한 서명이 없습니다. '

기본적으로 VB.NET 버전 2005 이하에서는 대리자가 변하지 않습니다. 사실 저는이 아이디어를 몇 년 전에 생각했지만 VB.NET이이 문제를 처리 할 수 ​​없었습니다 ...하지만 이제는 C #으로 확고하게 이동했으며 이제 VB.NET에서 처리 할 수 ​​있습니다. 이 게시물.

편집 : 업데이트 # 3

좋아, 나는 이것을 꽤 성공적으로 사용하고 있습니다. 정말 멋진 시스템입니다. 내 "StrongTypedEventHandler"의 이름을 "GenericEventHandler"로 지정하고 다음과 같이 정의했습니다.

[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
    TSender sender,
    TEventArgs e
)
where TEventArgs : EventArgs;

이 이름 변경 외에 위에서 설명한대로 정확하게 구현했습니다.

다음과 같은 FxCop 규칙 CA1009를 넘어갑니다.

"관습 적으로 .NET 이벤트에는 이벤트 발신자와 이벤트 데이터를 지정하는 두 개의 매개 변수가 있습니다. 이벤트 핸들러 서명은 void MyEventHandler (object sender, EventArgs e)) 형식을 따라야합니다. 'sender'매개 변수는 항상 System.Object 유형입니다. 보다 구체적인 형식을 사용할 수있는 경우에도 'e'매개 변수는 항상 System.EventArgs 형식입니다. 이벤트 데이터를 제공하지 않는 이벤트는 System.EventHandler 대리자 형식을 사용해야합니다. 이벤트 처리기는 보낼 수 있도록 void를 반환합니다. 각 이벤트를 여러 대상 메서드에 추가합니다. 대상에서 반환 한 모든 값은 첫 번째 호출 후 손실됩니다. "

물론 우리는이 모든 것을 알고 있으며 어쨌든 규칙을 위반하고 있습니다. (모든 이벤트 핸들러는 어떤 경우 에든 선호하는 경우 서명에 표준 '객체 발신자'를 사용할 수 있습니다. 이것은 비파괴적인 변경입니다.)

따라서 a의 사용은 SuppressMessageAttribute트릭을 수행합니다.

[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
    Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]

이 접근 방식이 언젠가는 표준이되기를 바랍니다. 정말 잘 작동합니다.

모든 의견 주셔서 감사합니다. 정말 감사합니다 ...

마이크


6
해. (이것이 대답을 정당화한다고 생각하지 마십시오.)
Konrad Rudolph

1
내 주장은 실제로 당신에게 지적되지 않았습니다. 물론 당신은 당신의 프로젝트에서 이것을해야합니다. 그들은 왜 그것이 BCL에서 작동하지 않을지에 대한 논쟁이었습니다.
Tommy Carlier

3
야, 내 프로젝트가 처음부터이 작업을 수행했으면 좋겠어. 보낸 사람을 캐스팅하는 것이 싫다.
Matt H

7
이제 본은 질문이다. 여러분? 이 트윗 크기의 oh hi this my hom work solve it plz :code dump:질문 중 하나가 아니라에서 배운 질문 입니다 .
Camilo Martin

3
또 다른 제안 EventHandler<,>GenericEventHandler<,>. EventHandler<>BCL 에는 이미 EventHandler라는 일반이 있습니다 . 그래서 EventHandler는 더 일반적인 이름이고 델리게이트는 타입 오버로드를 지원합니다
nawfal

답변:


25

유사한 예가 이제 MSDN에 있으므로 Microsoft가 이것을 선택 한 것 같습니다.

일반 대리인


2
+1 아, 훌륭합니다. 그들은 실제로 이것을 선택했습니다. 이것은 좋습니다. 그것은 지금처럼, 등 인텔리의 측면에서이 패턴을 사용하는 것이 더 어색, 때문에, 그들은 VS IDE 내에서이 인식 패턴을 만드는 것이하지만, 희망
마이크 로젠 블럼

13

당신이 제안하는 것은 실제로 많은 의미가 있습니다. 그리고 저는 이것이 원래 제네릭 이전에 디자인 되었기 때문에 단순한 방식 인 그런 것들 중 하나인지 아니면 진짜 이유가 있는지 궁금합니다.


1
이것이 바로 그 이유라고 확신합니다. 그러나 이제 최신 버전의 언어는이를 처리하기 위해 반 변성을 가지고 있으므로 이전 버전과 호환되는 방식으로이를 처리 할 수 ​​있어야합니다. '보낸 사람 개체'를 사용하는 이전 처리기는 중단되지 않습니다. 그러나 이것은 이전 언어에는 해당되지 않으며 일부 현재 .NET 언어에는 해당되지 않을 수 있습니다. 확실하지 않습니다.
Mike Rosenblum

13

WinRT (Windows 런타임) TypedEventHandler<TSender, TResult>는 정확히 수행하는 작업을 StrongTypedEventHandler<TSender, TResult>수행하지만 TResult유형 매개 변수 에 대한 제약 조건이없는 대리자를 도입합니다 .

public delegate void TypedEventHandler<TSender, TResult>(TSender sender,
                                                         TResult args);

MSDN 설명서는 여기에 있습니다 .


1
아, 진전이 있다는 것을 알게되어 반갑습니다 ... 왜 TResult가 'EventArgs'클래스에서 상속하도록 제한되지 않는지 궁금합니다. 'EventArgs'기본 클래스는 기본적으로 비어 있습니다. 이 제한에서 벗어나고있는 것일까 요?
Mike Rosenblum 2012 년

디자인 팀의 감독 일 수 있습니다. 누가 알아.
Pierre Arnaud

음, 이벤트는를 사용하지 않아도 잘 작동합니다. EventArgs단지 관례적인 것입니다
세바스찬

3
그것은 특히 그 TypedEventHandler 설명서에 명시 args될 것입니다 null어떤 이벤트 데이터가없는 경우 그들이 멀리 기본적으로 본질적으로 빈 객체를 사용에서 점점 것처럼 않도록. 원래 아이디어는 EventArgs유형이 항상 호환되기 때문에 유형의 두 번째 매개 변수가있는 메소드가 모든 이벤트를 처리 할 수 ​​있다는 것이 었습니다. 그들은 아마도 하나의 방법으로 여러 다른 이벤트를 처리 할 수 ​​있다는 것이 그다지 중요하지 않다는 것을 깨닫고있을 것입니다.
jmcilhinney 2014

1
그것은 감독처럼 보이지 않습니다. 제약 조건은 System.EventHandler <TEventArgs> 대리자에서도 제거되었습니다. referencesource.microsoft.com/#mscorlib/system/…
colton7909 2017

5

나는 다음 진술에 문제가 있습니다.

  • 2005 년까지의 이전 버전의 Visual Basic .NET에는 대리자 공분산과 반공 변성이 없습니다.
  • 나는 이것이 신성 모독에 가깝다는 것을 완전히 알고 있습니다.

우선, 여기서 수행 한 작업은 공분산 또는 반공 분산과 관련이 없습니다. ( 편집 : 이전 문장이 잘못되었습니다. 자세한 내용 은 대리자의 공분산 및 반 분산을 참조하십시오 )이 솔루션은 모든 CLR 버전 2.0 이상에서 잘 작동합니다 (분명히 제네릭을 사용하므로 CLR 1.0 응용 프로그램 에서는 작동 하지 않습니다 ).

둘째, 당신의 아이디어가 "신성 모독"에 가깝다는 점에 강력히 동의하지 않습니다.


2
안녕 앤드류, 엄지 척 감사합니다! 귀하의 평판 수준을 고려할 때 이것은 실제로 많은 것을 의미합니다. 공분산 / 반 변성 문제에 대해 : 구독자가 제공 한 대리자가 게시자 이벤트의 서명과 정확히 일치하지 않으면 공분산과 반 반성이 관련됩니다. C #은 영원히 대리자 공분산과 반공 변성을 가지고 있으므로 내재적으로 느껴지지만 VB.NET은 .NET 3.0까지 대리자 공분산과 반공 변성을 갖지 않았습니다. 따라서 VB.NET for .NET 2.0 이하에서는이 시스템을 사용할 수 없습니다. (위의 "Edit # 2"에서 추가 한 코드 예제를 참조하십시오.)
Mike Rosenblum

@Mike-내 사과, 당신은 100 % 맞습니다! 나는 당신의 포인트 :) 반영하기 위해 내 대답을 편집 한
앤드류 헤어

4
아, 흥미 롭군요! 대리자 공분산 / 반공 변성은 CLR의 일부인 것 같지만 (모르는 이유로) VB.NET에서 최신 버전 이전에 노출되지 않았습니다. 다음은 언어 자체에서 활성화되지 않은 경우 Reflection을 사용하여 위임 분산을 달성 할 수있는 방법을 보여주는 Francesco Balena의 기사입니다. dotnet2themax.com/blogs/fbalena/… .
Mike Rosenblum

1
@Mike-CLR이 지원하지만 .NET 언어에서 지원되지 않는 것을 배우는 것은 항상 흥미 롭습니다.
Andrew Hare

5

나는 이것이 새로운 WinRT로 어떻게 처리되는지 그리고 여기의 다른 의견을 기반으로 훑어 보았고 마침내 다음과 같이하기로 결정했습니다.

[Serializable]
public delegate void TypedEventHandler<in TSender, in TEventArgs>(
    TSender sender,
    TEventArgs e
) where TEventArgs : EventArgs;

이것은 WinRT에서 TypedEventHandler라는 이름을 사용하는 것을 고려할 때 가장 좋은 방법 인 것 같습니다.


TEventArgs에 일반 제한을 추가하는 이유는 무엇입니까? 실제로 의미가 없기 때문에 EventHandler <> 및 TypedEventHandler <,>에서 제거되었습니다.
Mike Marynowski

2

나는 그것이 좋은 아이디어라고 생각하며 MS는 예를 들어 ArrayList에서 일반 기반 목록으로 이동할 때와 같이 더 나은 것을 만드는 데 투자 할 시간이나 관심이 없을 수 있습니다.


당신이 옳을 수도 있습니다 ... 반면에, 저는 이것이 단지 "표준"일 뿐이며 아마도 기술적 인 문제가 아닐 수도 있습니다. 즉,이 기능은 현재의 모든 .NET 언어에있을 수 있지만 모르겠습니다. 나는 C #과 VB.NET이 이것을 처리 할 수 ​​있다는 것을 알고 있습니다. 그러나 이것이 현재의 모든 .NET 언어에서 얼마나 광범위하게 작동하는지 잘 모르겠습니다 ...하지만 C #과 VB.NET에서 작동하고 여기에있는 모든 사람들이 매우지지하기 때문에이 작업을 할 가능성이 매우 높습니다. :-)
Mike Rosenblum

2

내가 이해 한 바에 따르면 "Sender"필드는 항상 이벤트 구독을 보유하는 개체를 참조해야합니다. 내 druthers가 있었다면 이벤트가 필요 해지면 (*) 이벤트를 구독 취소하기에 충분한 정보를 보유한 필드도있을 것입니다 (예 : '컬렉션 변경'이벤트를 구독하는 변경 로거를 고려하십시오. 하나는 실제 작업을 수행하고 실제 데이터를 보유하고 다른 하나는 공용 인터페이스 래퍼를 제공하며 주요 부분은 래퍼 부분에 대한 약한 참조를 보유 할 수 있습니다. 래퍼 부분이 가비지 수집되면 이는 의미합니다. 더 이상 수집 된 데이터에 관심이있는 사람이 없었으므로 변경 로거는 수신하는 모든 이벤트에서 구독을 취소해야합니다.

개체가 다른 개체를 대신하여 이벤트를 보낼 수 있기 때문에 개체 유형의 "sender"필드가 있고 EventArgs 파생 필드에 개체에 대한 참조가 포함되도록하는 데 대한 잠재적 인 유용성을 볼 수 있습니다. 행동을 취하십시오. 그러나 "sender"필드의 유용성은 개체가 알 수없는 보낸 사람의 구독을 취소 할 수있는 깨끗한 방법이 없다는 사실에 의해 제한 될 수 있습니다.

(*) 사실, 구독 취소를 처리하는 더 깨끗한 방법은 Boolean을 반환하는 함수에 대한 멀티 캐스트 델리게이트 유형을 갖는 것입니다. 이러한 대리자가 호출 한 함수가 True를 반환하면 대리자는 해당 개체를 제거하도록 패치됩니다. 이는 델리게이트가 더 이상 불변성이 아니라 스레드로부터 안전한 방식으로 이러한 변경을 수행 할 수 있음을 의미합니다 (예 : 객체 참조를 무효화하고 멀티 캐스트 델리게이트 코드가 포함 된 null 객체 참조를 무시하도록 함). 이 시나리오에서는 이벤트의 출처에 관계없이 삭제 된 개체에 대한 게시 및 이벤트를 매우 깔끔하게 처리 할 수 ​​있습니다.


2

보낸 사람을 개체 유형으로 만드는 유일한 이유 (Microsoft의 실수 인 IMHO 인 VB 2005 코드에서 반 변성 문제를 생략하는 경우)로 신성 모독을 되돌아 보면, 적어도 두 번째 인수를 EventArgs 유형에 못 박는 이론적 동기를 제안 할 수 있습니다. 더 나아가이 특별한 경우에 Microsoft의 지침 및 규칙을 준수해야하는 타당한 이유가 있습니까?

이벤트 처리기 내부에서 전달하려는 다른 데이터에 대해 다른 EventArgs 래퍼를 개발해야하는 것은 이상해 보입니다. 왜 데이터를 바로 전달할 수 없습니다. 다음 코드 섹션을 고려하십시오.

[예제 1]

public delegate void ConnectionEventHandler(Server sender, Connection connection);

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, connection);
    }

    public event ConnectionEventHandler ClientConnected;
}

[예제 2]

public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);

public class ConnectionEventArgs : EventArgs
{
    public Connection Connection { get; private set; }

    public ConnectionEventArgs(Connection connection)
    {
        this.Connection = connection;
    }
}

public partial class Server
{
    protected virtual void OnClientConnected(Connection connection)
    {
        if (ClientConnected != null) ClientConnected(this, new ConnectionEventArgs(connection));
    }

    public event ConnectionEventHandler ClientConnected;
}

2
예, System.EventArgs에서 상속하는 별도의 클래스를 만드는 것은 직관적이지 않고 추가 작업을 나타내지 만 그럴만 한 이유가 있습니다. 코드를 변경할 필요가 없다면 접근 방식이 좋습니다. 그러나 현실은 향후 버전에서 이벤트의 기능을 높이고 이벤트 인수에 속성을 추가해야 할 수도 있다는 것입니다. 시나리오에서는 이벤트 처리기의 서명에 추가 오버로드 또는 선택적 매개 변수를 추가해야합니다. 이것은 VBA 및 레거시 VB 6.0에서 사용되는 접근 방식으로 실행 가능하지만 실제로는 약간 추합니다.
Mike Rosenblum

1
그러나 EventArgs에서 상속함으로써 향후 버전은 이전 이벤트 인수 클래스에서 상속하고이를 확장 할 수 있습니다. 모든 이전 호출자는 새 이벤트 인수 클래스의 기본 클래스에 대해 작동하여 그대로 정확하게 작동 할 수 있습니다. 매우 깨끗합니다. 더 많은 작업을 수행하지만 라이브러리에 의존하는 모든 발신자에게는 더 깨끗합니다.
Mike Rosenblum

상속 할 필요조차 없습니다. 이벤트 인수 클래스에 직접 추가 기능을 추가하기 만하면 계속 정상적으로 작동합니다. 즉, 많은 시나리오에서 의미가 없었기 때문에 eventargs에 args를 고정하는 제한이 제거되었습니다. 특정 이벤트의 기능을 확장 할 필요가 전혀 없다는 것을 알고 있거나 매우 성능에 민감한 애플리케이션에서 값 유형 인수 만 있으면됩니다.
Mike Marynowski

1

현재 상황 (발신자가 객체 임)에서는 여러 이벤트에 메서드를 쉽게 연결할 수 있습니다.

button.Click += ClickHandler;
label.Click += ClickHandler;

void ClickHandler(object sender, EventArgs e) { ... }

발신자가 일반인 경우 클릭 이벤트의 대상은 Button 또는 Label 유형이 아니라 Control 유형이됩니다 (이벤트가 Control에 정의되어 있기 때문). 따라서 Button 클래스의 일부 이벤트에는 Control 유형의 대상이 있고 다른 이벤트에는 다른 대상 유형이 있습니다.


2
토미, 당신은 할 수 정확히 내가 제안하고있어 그 시스템에이 같은 일을. 이러한 강력한 유형의 이벤트를 처리하기 위해 '개체 발신자'매개 변수가있는 표준 이벤트 처리기를 계속 사용할 수 있습니다. (원래 게시물에 추가 한 코드 예제를 참조하십시오.)
Mike Rosenblum

예, 동의합니다. 이것이 표준 .NET 이벤트에 대한 좋은 점입니다.
Lu4 2010

1

나는 당신이하고 싶은 일에 문제가 없다고 생각합니다. 대부분의 경우 object sender2.0 이전 코드를 계속 지원하기 위해 매개 변수가 남아 있다고 생각합니다 .

퍼블릭 API에 대해이 변경 사항을 적용하려면 고유 한 기본 EvenArgs 클래스를 만드는 것이 좋습니다. 이 같은:

public class DataEventArgs<TSender, TData> : EventArgs
{
    private readonly TSender sender, TData data;

    public DataEventArgs(TSender sender, TData data)
    {
        this.sender = sender;
        this.data = data;
    }

    public TSender Sender { get { return sender; } }
    public TData Data { get { return data; } }
}

그런 다음 다음과 같이 이벤트를 선언 할 수 있습니다.

public event EventHandler<DataEventArgs<MyClass, int>> SomeIndexSelected;

그리고 다음과 같은 방법 :

private void HandleSomething(object sender, EventArgs e)

계속 구독 할 수 있습니다.

편집하다

그 마지막 줄은 저를 조금 생각하게 만들었습니다. 런타임에는 매개 변수를 다운 캐스팅하는 데 문제가 없기 때문에 외부 기능을 손상시키지 않고 실제로 제안한 것을 구현할 수 있어야합니다. 나는 여전히DataEventArgs (개인적으로) 해결책에 입니다. 그러나 보낸 사람이 첫 번째 매개 변수와 이벤트 인수의 속성으로 저장되기 때문에 중복된다는 것을 알고 있으므로 그렇게 할 것입니다.

를 고수하는 한 가지 이점은 DataEventArgsEventArgs가 원래 보낸 사람을 유지하면서 보낸 사람을 변경하여 (마지막 보낸 사람을 나타 내기 위해) 이벤트를 연결할 수 있다는 것입니다.


이봐 마이클, 이것은 꽤 깔끔한 대안입니다. 나는 그것을 좋아한다. 하지만 언급했듯이 'sender'매개 변수를 효과적으로 두 번 전달하는 것은 중복됩니다. 유사한 접근 방식이 여기에 설명되어 있습니다. stackoverflow.com/questions/809609/… , 합의는 너무 비표준적인 것 같습니다. 이것이 제가 여기서 강력한 유형의 '보낸 사람'아이디어를 제안하는 것을 주저 한 이유입니다. (하지만 호평을받은 것 같아서 기쁩니다.)
Mike Rosenblum

1

그것을 위해 가십시오. 컴포넌트 기반이 아닌 코드의 경우 이벤트 서명을 단순화하여

public event Action<MyEventType> EventName

어디에서 MyEventType상속하지 않습니다 EventArgs. EventArgs의 멤버를 사용하지 않으려는 경우 왜 귀찮게해야합니까?


1
동의하다! 왜 우리 스스로 원숭이를 느껴야합니까?
Lu4 2010

1
+ 1-ed, 이것이 나도 사용하는 것입니다. 때때로 단순함이 승리합니다! 심지어 event Action<S, T>, event Action<R, S, T>등 난의 확장 방법이 Raise그들이 스레드 안전 :)
nawfal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.