programing

C # 이벤트 처리기에서 "sender"매개 변수가 개체 여야하는 이유는 무엇입니까?

nasanasas 2020. 11. 2. 08:04
반응형

C # 이벤트 처리기에서 "sender"매개 변수가 개체 여야하는 이유는 무엇입니까?


Microsoft 이벤트 명명 지침 에 따르면 senderC # 이벤트 처리기 매개 변수는 " 더 구체적인 유형을 사용할 수있는 경우에도 항상 객체 유형입니다".

이로 인해 다음과 같은 많은 이벤트 처리 코드가 생성됩니다.

RepeaterItem item = sender as RepeaterItem;
if (item != null) { /* Do some stuff */ }

규칙이 더 구체적인 유형으로 이벤트 핸들러를 선언하는 것을 권장하지 않는 이유는 무엇입니까?

MyType
{
    public event MyEventHander MyEvent;
}

...

delegate void MyEventHander(MyType sender, MyEventArgs e);

내가 뭔가를 놓치고 있습니까?

후손을 위해 : 나는 더 구체적인 유형을 사용할 수있을 때에도 객체를 사용하고를 통해 데이터를 전달 하는 것이 관례 이며EventArgs 실제 프로그래밍 에서 따르는 것이 중요하다는 일반적인 정서에 동의 합니다. 컨벤션.

편집 : 검색 미끼 : RSPEC-3906 규칙 "이벤트 처리기는 올바른 서명을 가져야합니다."


음, 그것은 규칙 이라기보다는 패턴입니다. 이는 한 구성 요소가 이벤트를 발생시키는 정상적인 유형이 아니더라도 원래 보낸 사람을 유지하면서 다른 구성 요소에서 이벤트를 전달할 수 있음을 의미합니다.

나는 그것이 약간 이상하다는 데 동의합니다. 그러나 그것은 아마도 친숙 함을 위해 컨벤션을 고수 할 가치가있을 것입니다. (다른 개발자에 대한 친숙 함, 즉) 나는 특별히 EventArgs나 자신 에 대해 열중 한 적이 없었지만 (그 자체로는 정보를 전달하지 않는다는 점을 감안할 때), 그것은 또 다른 주제입니다. (적어도 EventHandler<TEventArgs>지금은 있습니다 EventArgs<TContent>. 전파 할 단일 값만 필요한 일반적인 상황에 대해서도 도움이 될 것 입니다.)

편집 : 물론 대리자를 더 일반적인 목적으로 만듭니다-단일 대리자 유형을 여러 이벤트에서 재사용 할 수 있습니다. 특히 제네릭에 비추어 볼 때 특히 좋은 이유로 그것을 구입했는지는 모르겠지만 뭔가가 있다고 생각합니다 ...


이 대회에는 좋은 이유가 있다고 생각합니다.

@erikkallen의 예를 들어 보겠습니다.

void SomethingChanged(object sender, EventArgs e) {
    EnableControls();
}
...
MyRadioButton.Click += SomethingChanged;
MyCheckbox.Click += SomethingChanged;
MyDropDown.SelectionChanged += SomethingChanged;
...

공분산이 지원되기 때문에 가능합니다 (그리고 제네릭 이전 .Net 1 이후).

하향식으로 진행하는 경우 질문은 완전히 의미가 있습니다. 즉, 코드에 이벤트가 필요하므로 컨트롤에 추가합니다.

그러나 규칙은 처음부터 구성 요소를 작성할 때 더 쉽게 만드는 것입니다. 모든 이벤트에 대해 기본 패턴 (개체 발신자, EventArgs e)이 작동 한다는 것을 알고 있습니다.

이벤트를 추가 할 때 어떻게 사용되는지 알 수 없으며 구성 요소를 사용하여 개발자를 임의로 제한하고 싶지 않습니다.

강력하게 형식이 지정된 일반적인 이벤트의 예는 코드에 적합하지만 다른 개발자가 작성한 다른 구성 요소와는 맞지 않습니다. 예를 들어 위의 구성 요소와 함께 구성 요소를 사용하려는 경우 :

//this won't work
GallowayClass.Changed += SomethingChanged;

이 예에서 추가 유형 제약 조건은 원격 개발자에게 고통을주는 것입니다. 이제 구성 요소에 대한 새 대리자를 만들어야합니다. 구성 요소 부하를 사용하는 경우 각 구성 요소에 대한 델리게이트가 필요할 수 있습니다.

나는 컨벤션이 외부의 모든 것에 대해 따를 가치가 있거나 가까운 팀 외부에서 사용될 것으로 예상한다고 생각합니다.

일반적인 이벤트 인수의 아이디어가 마음에 듭니다. 이미 비슷한 것을 사용하고 있습니다.


강력한 형식의 보낸 사람을 선호 할 때 다음 대리자를 사용합니다.

/// <summary>
/// Delegate used to handle events with a strongly-typed sender.
/// </summary>
/// <typeparam name="TSender">The type of the sender.</typeparam>
/// <typeparam name="TArgs">The type of the event arguments.</typeparam>
/// <param name="sender">The control where the event originated.</param>
/// <param name="e">Any event arguments.</param>
public delegate void EventHandler<TSender, TArgs>(TSender sender, TArgs e) where TArgs : EventArgs;

다음과 같은 방식으로 사용할 수 있습니다.

public event EventHandler<TypeOfSender, TypeOfEventArguments> CustomEvent;

제네릭과 히스토리는 특히 유사한 이벤트를 노출하는 컨트롤 (기타)의 수에서 큰 역할을합니다. 제네릭이 없으면 많은 이벤트가 노출 Control되어 거의 쓸모가 없습니다.

  • 유용한 작업을 수행하려면 여전히 캐스트해야합니다 (를 사용하여 수행 할 수있는 참조 검사 제외 object).
  • 비 컨트롤에서 이벤트를 재사용 할 수 없습니다.

제네릭을 고려하면 다시 모든 것이 잘되지만 상속 문제가 발생하기 시작합니다. 클래스 B : A이면 이벤트가 A있어야 EventHandler<A, ...>하고 이벤트가 B있어야 EventHandler<B, ...>합니까? 다시 말하지만, 매우 혼란스럽고 툴링이 어렵고 언어 측면에서 약간 지저분합니다.

이 모든 것을 다루는 더 나은 옵션이있을 때까지 object작동합니다. 이벤트는 거의 항상 클래스 인스턴스에 있으므로 권투 등이 없습니다. 그리고 캐스팅은 그리 느리지 않습니다.


다음과 같은 일을 할 수 있어야하기 때문인 것 같습니다.

void SomethingChanged(object sender, EventArgs e) {
    EnableControls();
}
...
MyRadioButton.Click += SomethingChanged;
MyCheckbox.Click += SomethingChanged;
...

코드에서 안전한 캐스트를하는 이유는 무엇입니까? 리피터에 대한 이벤트 핸들러로만 함수를 사용한다는 것을 알고 있다면 인수가 항상 올바른 유형이고 대신 던지기 캐스트를 사용할 수 있습니다 (예 : (sender as Repeater) 대신 (Repeater) sender).


그럴만 한 이유가 없습니다. 이제 공변 성과 반 변성이 있습니다. 강력한 유형의 Sender를 사용하는 것이 좋습니다. 질문에 대한 토론보기


규칙은 일관성을 부여하기 위해서만 존재합니다.

You CAN strongly type your event handlers if you wish, but ask yourself if doing so would provide any technical advantage?

You should consider that event handlers don't always need to cast the sender... most of the event handling code I've seen in actual practice don't make use of the sender parameter. It is there IF it is needed, but quite often it isn't.

I often see cases where different events on different objects will share a single common event handler, which works because that event handler isn't concerned with who the sender was.

If those delegates were strongly typed, even with clever use of generics, it would be VERY difficult to share an event handler like that. In fact, by strongly typing it you are imposing the assumption that the handlers should care what the sender is, when that isn't the practical reality.

I guess what you should be asking is why WOULD you strongly type the event handling delegates? By doing so would you be adding any significant functional advantages? Are you making the usage more "consistent"? Or are you just imposing assumptions and constraints just for the sake of strong-typing?


You say:

This leads to lots of event handling code like:-

RepeaterItem item = sender as RepeaterItem
if (RepeaterItem != null) { /* Do some stuff */ }

Is it really lots of code?

I'd advise never to use the sender parameter to an event handler. As you've noticed, it's not statically typed. It's not necessarily the direct sender of the event, because sometimes an event is forwarded. So the same event handler may not even get the same sender object type every time it is fired. It's an unnecessary form of implicit coupling.

When you enlist with an event, at that point you must know what object the event is on, and that is what you're most likely to be interested in:

someControl.Exploded += (s, e) => someControl.RepairWindows();

And anything else specific to the event ought to be in the EventArgs-derived second parameter.

Basically the sender parameter is a bit of historical noise, best avoided.

I asked a similar question here.


It's because you can never be sure who fired the event. There is no way to restrict which types are allowed to fire a certain event.


The pattern of using EventHandler(object sender, EventArgs e) is meant to provide for all events the means of identifying the event source (sender), and providing a container for all the event's specific payload. The advantage of this pattern is also that it allows to generate a number of different events using the same type of delegate.

As for the arguments of this default delegate... The advantage of having a single bag for all the state you want to pass along with the event is fairly obvious, especially if there are many elements in that state. Using object instead of a strong type allows to pass the event along, possibly to assemblies that do not have a reference to your type (in which case you may argue that they won't be able to use the sender anyway, but that's another story - they can still get the event).

In my own experience, I agree with Stephen Redd, very often the sender is not used. The only cases I've needed to identify the sender is in the case of UI handlers, with many controls sharing the same event handler (to avoid duplicating code). I depart from his position, however, in that I see no problem defining strongly typed delegates, and generating events with strongly typed signatures, in the case where I know that the handler will never care who the sender is (indeed, often it should not have any scope into that type), and I do not want the inconvenience of stuffing state into a bag (EventArg subclass or generic) and unpacking it. If I only have 1 or 2 elements in my state, I'm OK generating that signature. It's a matter of convenience for me: strong typing means the compiler keeps me on my toes, and it reduces the kind of branching like

Foo foo = sender as Foo;
if (foo !=null) { ... }

which does make the code look better :)

This being said, it is just my opinion. I've deviated often from the recommended pattern for events, and I have not suffered any for it. It is important to always be clear about why it is OK to deviate from it. Good question! .


Well, that's a good question. I think because any other type could use your delegate to declare an event, so you can't be sure that the type of the sender is really "MyType".


I tend to use a specific delegate type for each event (or a small group of similar events). The useless sender and eventargs simply clutter the api and distract from the actually relevant bits of information. Being able to "forward" events across classes isn't something I've yet to find useful - and if you're forwarding events like that, to an event handler that represents a different type of event, then being forced to wrap the event yourself and provide the appropriate parameters is little effort. Also, the forwarder tends to have a better idea of how to "convert" the event parameters than the final receiver.

In short, unless there's some pressing interop reason, dump the useless, confusing parameters.

참고URL : https://stackoverflow.com/questions/1437699/in-a-c-sharp-event-handler-why-must-the-sender-parameter-be-an-object

반응형