나는이 질문이 10 세 이상이라는 것을 알고 있지만, 가장 분명한 대답이 해결되지 않았을뿐만 아니라 그 질문에서 다루는 내용을 잘 이해하지 못하는 것 같습니다. 또한, 늦게 구속되는 것에 대한 다른 질문들과 그것이 델리게이트와 람다와 관련하여 의미하는 것이 있습니다 (나중에 더 자세히 설명합니다).
먼저 방에서 800 파운드 코끼리 / 고릴라를 다루기 위해 eventvs 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와 같은 프레임 워크를 사용할 때 종종 이것을 볼 수 있습니다. 람다와는 달리 람다와는 달리 한 번 이상 발생할 수있는 위임의 늦은 바인딩과는 매우 다릅니다 (즉, 람다는 항상 존재하지만 필요할 때마다 바인딩이 필요합니다). -마법이 사라지고, 방법 / 속성 (들)은 항상 구속력이 있습니다. 명심해야 할 것.