선언 사이에 다른 거기에 event Action<>
하고 event EventHandler<>
.
어떤 객체가 실제로 이벤트를 발생시키는 것이 중요하지 않다고 가정합니다.
예를 들면 다음과 같습니다.
public event Action<bool, int, Blah> DiagnosticsEvent;
vs
public event EventHandler<DiagnosticsArgs> DiagnosticsEvent;
class DiagnosticsArgs : EventArgs
{
public DiagnosticsArgs(bool b, int i, Blah bl)
{...}
...
}
두 경우 모두 사용법이 거의 동일합니다.
obj.DiagnosticsEvent += HandleDiagnosticsEvent;
event EventHandler<>
패턴 에 대해 싫어하는 몇 가지 사항이 있습니다 .
- EventArgs에서 파생 된 추가 유형 선언
- 객체 소스의 강제 전달 – 아무도 신경 쓰지 않음
코드가 많을수록 분명한 이점없이 유지해야 할 코드가 많아집니다.
결과적으로, 나는 선호한다 event Action<>
그러나 Action <>에 유형 인수가 너무 많은 경우에만 추가 클래스가 필요합니다.
답변
가장 큰 차이점은 Action<>
이벤트 를 사용 하면 시스템의 다른 이벤트의 디자인 패턴을 따르지 않는다는 것입니다.
지배적 인 디자인 패턴 (동일성의 힘을 제외하고)의 한 가지 단점 EventArgs
은 이벤트의 서명을 변경하지 않고도 새로운 속성으로 객체를 확장 할 수 있다는 것 입니다. 를 사용하면 여전히 가능 Action<SomeClassWithProperties>
하지만 실제로는 일반적인 접근 방식을 사용하지 않는 것이 중요하지 않습니다.
답변
이전 답변 중 일부를 기반으로 답변을 세 가지 영역으로 나누겠습니다.
먼저, Action<T1, T2, T2... >
파생 클래스를 사용하는 것과 의 물리적 제한 이 EventArgs
있습니다. 세 가지가 있습니다. 첫째, 매개 변수의 수 또는 유형을 변경하는 경우 구독하는 모든 메소드를 새 패턴에 맞게 변경해야합니다. 제 3 자 어셈블리가 사용할 공개 이벤트이고 이벤트 인수가 변경 될 가능성이있는 경우, 일관성을 위해 이벤트 인수에서 파생 된 사용자 정의 클래스를 사용해야하는 이유입니다 (여전히 귀하는 COULD 할 수 있음을 기억하십시오) 사용 Action<MyCustomClass>
하여,) 둘째 Action<T1, T2, T2... >
는 예를 들어 처리 된 속성 객체의 어떤 종류 (하지 않는 한 동작과 함께 전달되는) 호출 방법 피드백 BACK을 통과하지 못할 것입니다. 세 번째는 3을 전달하는 그렇다면, 당신은 매개 변수를 명명되지 않은이 bool
‘는 S int
, 두string
와 DateTime
, 당신은 그 값들의 의미가 무엇인지 전혀 모른다. 참고로 “여전히 이벤트를 안전하게 사용하면서이 이벤트를 안전하게 실행”할 수 있습니다 Action<T1, T2, T2... >
.
둘째, 일관성 함의. 이미 작업중인 대형 시스템이있는 경우, 정당한 이유가없는 한 시스템의 나머지 부분이 설계된 방식을 따르는 것이 거의 항상 좋습니다. 유지 관리해야하는 이벤트에 공개적으로 직면 한 경우 파생 클래스를 대체하는 기능이 중요 할 수 있습니다. 명심하십시오.
세 번째로, 실제 실습에서는 개인적으로 상호 작용해야하는 속성 변경 (특히 상호 작용하는 뷰 모델을 사용하여 MVVM을 수행 할 때) 또는 이벤트가있는 위치에 대해 많은 일회성 이벤트를 생성하는 경향이 있음을 개인적으로 발견했습니다. 단일 매개 변수. 대부분의 경우 이러한 이벤트는 public event Action<[classtype], bool> [PropertyName]Changed;
또는 형식입니다 public event Action SomethingHappened;
. 이 경우 두 가지 이점이 있습니다. 먼저 발급 클래스의 유형을 얻습니다. 경우 MyClass
선언하고 이벤트를 트리거하는 유일한 클래스, 난의 명시 적 인스턴스를 얻을 MyClass
이벤트 핸들러에 대한 작업. 둘째, 속성 변경 이벤트와 같은 간단한 이벤트의 경우 매개 변수의 의미가 명확하고 이벤트 처리기 이름으로 명시되어 있으며 이러한 종류의 이벤트에 대해 수많은 클래스를 만들 필요가 없습니다.
답변
대부분의 경우 패턴을 따릅니다. 나는 한 그것에서 이탈,하지만 아주 드물게, 특정 이유로. 필자의 경우 가장 큰 문제는 아마도 여전히을 사용하여 Action<SomeObjectType>
나중에 추가 속성을 추가하고 가끔 2 웨이 속성 (생각 Handled
또는 다른 피드백 이벤트를 사용할 수 있음)입니다. 가입자 는 이벤트 객체에서 속성 을 설정 해야 합니다). 그리고 일단 당신이 그 라인을 시작하면, 당신은 EventHandler<T>
일부를 사용할 수도 있습니다 T
.
답변
코드가 300,000 개의 라인 프로젝트 내에있을 때보다 간결한 접근 방식의 이점이 있습니다.
이 작업을 사용하면 bool, int 및 Blah가 무엇인지 말해 줄 방법이 없습니다. 조치가 매개 변수를 정의한 오브젝트를 전달한 경우 ok입니다.
EventArgs를 원하는 EventHandler를 사용하고 목적을 주석 처리 한 특성에 대한 getter를 사용하여 DiagnosticsArgs 예제를 완료하면 더 이해하기 쉽습니다. 또한 DiagnosticsArgs 생성자에서 인수를 주석하거나 완전히 이름을 지정하십시오.
답변
표준 이벤트 패턴을 따르는 경우 확장 방법을 추가하여 이벤트 발생을보다 안전하고 쉽게 확인할 수 있습니다. 다음 코드는 null 검사를 수행하는 SafeFire ()라는 확장 메서드를 추가하고 이벤트에 영향을 줄 수있는 일반적인 null 경쟁 조건에서 안전하도록 이벤트를 별도의 변수에 복사합니다.
(널 객체에 확장 메서드를 사용 해야하는지 여부는 두 가지입니다.)
public static class EventFirer
{
public static void SafeFire<TEventArgs>(this EventHandler<TEventArgs> theEvent, object obj, TEventArgs theEventArgs)
where TEventArgs : EventArgs
{
if (theEvent != null)
theEvent(obj, theEventArgs);
}
}
class MyEventArgs : EventArgs
{
// Blah, blah, blah...
}
class UseSafeEventFirer
{
event EventHandler<MyEventArgs> MyEvent;
void DemoSafeFire()
{
MyEvent.SafeFire(this, new MyEventArgs());
}
static void Main(string[] args)
{
var x = new UseSafeEventFirer();
Console.WriteLine("Null:");
x.DemoSafeFire();
Console.WriteLine();
x.MyEvent += delegate { Console.WriteLine("Hello, World!"); };
Console.WriteLine("Not null:");
x.DemoSafeFire();
}
}
답변
나는이 질문이 10 세 이상이라는 것을 알고 있지만, 가장 분명한 대답이 해결되지 않았을뿐만 아니라 그 질문에서 다루는 내용을 잘 이해하지 못하는 것 같습니다. 또한, 늦게 구속되는 것에 대한 다른 질문들과 그것이 델리게이트와 람다와 관련하여 의미하는 것이 있습니다 (나중에 더 자세히 설명합니다).
먼저 방에서 800 파운드 코끼리 / 고릴라를 다루기 위해 event
vs Action<T>
/ Func<T>
:
- 하나의 명령문 또는 메소드를 실행하려면 람다를 사용하십시오.
event
실행할 여러 명령문 / 람다 / 함수를 가진 펍 / 서브 모델을 더 원할 때 사용하십시오 (이것은
박쥐 와 큰 차이입니다). - 명령문 / 함수를 표현식 트리로 컴파일하려면 람다를 사용하십시오. 리플렉션 및 COM interop에 사용되는 것과 같은보다 전통적인 늦은 바인딩에 참여하려면 대리인 / 이벤트를 사용하십시오.
이벤트의 예로 작은 콘솔 응용 프로그램을 사용하여 다음과 같이 단순하고 ‘표준적인’이벤트 집합을 연결합니다.
public delegate void FireEvent(int num);
public delegate void FireNiceEvent(object sender, SomeStandardArgs args);
public class SomeStandardArgs : EventArgs
{
public SomeStandardArgs(string id)
{
ID = id;
}
public string ID { get; set; }
}
class Program
{
public static event FireEvent OnFireEvent;
public static event FireNiceEvent OnFireNiceEvent;
static void Main(string[] args)
{
OnFireEvent += SomeSimpleEvent1;
OnFireEvent += SomeSimpleEvent2;
OnFireNiceEvent += SomeStandardEvent1;
OnFireNiceEvent += SomeStandardEvent2;
Console.WriteLine("Firing events.....");
OnFireEvent?.Invoke(3);
OnFireNiceEvent?.Invoke(null, new SomeStandardArgs("Fred"));
//Console.WriteLine($"{HeightSensorTypes.Keyence_IL030}:{(int)HeightSensorTypes.Keyence_IL030}");
Console.ReadLine();
}
private static void SomeSimpleEvent1(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent1)}:{num}");
}
private static void SomeSimpleEvent2(int num)
{
Console.WriteLine($"{nameof(SomeSimpleEvent2)}:{num}");
}
private static void SomeStandardEvent1(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent1)}:{args.ID}");
}
private static void SomeStandardEvent2(object sender, SomeStandardArgs args)
{
Console.WriteLine($"{nameof(SomeStandardEvent2)}:{args.ID}");
}
}
결과는 다음과 같습니다.
당신이 동일 한 경우 Action<int>
또는 Action<object, SomeStandardArgs>
, 당신은 단지 볼 것이다 SomeSimpleEvent2
하고 SomeStandardEvent2
.
내부에서 무슨 일이 event
?
확장 FireNiceEvent
하면 컴파일러가 실제로 다음을 생성합니다 (이 토론과 관련이없는 스레드 동기화와 관련하여 일부 세부 사항은 생략했습니다).
private EventHandler<SomeStandardArgs> _OnFireNiceEvent;
public void add_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Combine(_OnFireNiceEvent, handler);
}
public void remove_OnFireNiceEvent(EventHandler<SomeStandardArgs> handler)
{
Delegate.Remove(_OnFireNiceEvent, handler);
}
public event EventHandler<SomeStandardArgs> OnFireNiceEvent
{
add
{
add_OnFireNiceEvent(value)
}
remove
{
remove_OnFireNiceEvent(value)
}
}
컴파일러는 생성 된 클래스 네임 스페이스에 표시되지 않는 개인 대리자 변수를 생성합니다. 그 대의원은 구독 관리 및 후기 바인딩 참여에 사용되며 대중을 향한 인터페이스는 우리 모두가 알고 사랑하는 친숙 +=
하고 -=
운영자입니다.)
FireNiceEvent
델리게이트 의 범위 를 protected 로 변경하여 추가 / 제거 핸들러의 코드를 사용자 정의 할 수 있습니다 . 이를 통해 개발자는 로깅 또는 보안 후크와 같은 후크에 사용자 정의 후크를 추가 할 수 있습니다. 이것은 실제로 사용자 역할 등을 기반으로 구독에 대한 사용자 정의 된 접근성을 허용하는 매우 강력한 기능을 제공합니다. 람다로 그렇게 할 수 있습니까? (실제로 표현식 트리를 사용자 정의 컴파일하여 수행 할 수 있지만이 응답의 범위를 벗어납니다).
여기에 몇 가지 답변에서 몇 가지 요점을 설명하십시오.
-
인수 목록을
Action<T>
변경하고에서 파생 된 클래스의 속성을 변경하는 것 사이의 ‘취약성’에는 실제로 차이가 없습니다EventArgs
. 컴파일 변경이 필요할뿐 아니라 공용 인터페이스도 변경되고 버전 관리가 필요합니다. 차이 없음. -
산업 표준은 어느 곳에서 사용되는지에 따라 다릅니다.
Action<T>
IoC 및 DI에서event
종종 사용되며 GUI 및 MQ 유형 프레임 워크와 같은 메시지 라우팅에서 종종 사용됩니다. 참고 내가 말한 것을 종종 이 아니라 항상 . -
대표단은 람다와는 다른 수명을 가지고 있습니다. 또한 폐쇄에 대한 것이 아니라 ‘고양이가 끌린 모습을 본다’는 개념에 대한 인식도 알아야한다. 이것은 메모리 공간 / 수명 및 관리 누출에 영향을 미칩니다.
한 가지 더, 내가 이전에 언급 한 것 … 늦은 바인딩의 개념. 람다가 ‘라이브’되는 시점과 관련하여 LINQ와 같은 프레임 워크를 사용할 때 종종 이것을 볼 수 있습니다. 람다와는 달리 람다와는 달리 한 번 이상 발생할 수있는 위임의 늦은 바인딩과는 매우 다릅니다 (즉, 람다는 항상 존재하지만 필요할 때마다 바인딩이 필요합니다). -마법이 사라지고, 방법 / 속성 (들)은 항상 구속력이 있습니다. 명심해야 할 것.
답변
보면 표준 .NET 이벤트 패턴 우리가 발견
.NET 이벤트 델리게이트의 표준 서명은 다음과 같습니다.
void OnEventRaised(object sender, EventArgs args);
[…]
인수 목록에는 두 개의 인수 , sender 및 이벤트 인수가 포함됩니다. 보낸 사람의 컴파일 시간 유형 은 항상 올바른 파생 형식을 알고 있지만 System.Object 입니다. 규칙에 따라 object를 사용하십시오 .
동일한 페이지에서 다음과 같은 일반적인 이벤트 정의의 예를 찾을 수 있습니다.
public event EventHandler<EventArgs> EventName;
우리가 정의했다
class MyClass
{
public event Action<MyClass, EventArgs> EventName;
}
처리기는
void OnEventRaised(MyClass sender, EventArgs args);
여기서 sender
올바른 (가지고 더 유래 ) 타입.