이 관용구에 대한 몇 가지 언급을 보았습니다 ( SO 포함 ) :
// Deliberately empty subscriber
public event EventHandler AskQuestion = delegate {};
장점은 분명합니다. 이벤트를 발생시키기 전에 null을 확인할 필요가 없습니다.
그러나 단점이 있는지 이해하고 싶습니다. 예를 들어, 널리 사용되고 있고 유지 관리 문제를 일으키지 않을만큼 투명한 것입니까? 빈 이벤트 구독자 호출로 인해 눈에 띄는 성능 저하가 있습니까?
답변
유일한 단점은 여분의 빈 델리게이트를 호출 할 때 매우 약간의 성능 저하입니다. 그 외에는 유지 보수 패널티 또는 기타 단점이 없습니다.
답변
성능 오버 헤드를 유발하는 대신 확장 방법 을 사용하여 두 문제를 모두 완화 하는 것이 좋습니다 .
public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
if(handler != null)
{
handler(sender, e);
}
}
정의 된 후에는 다시 null 이벤트 검사를 수행 할 필요가 없습니다.
// Works, even for null events.
MyButtonClick.Raise(this, EventArgs.Empty);
답변
시스템의 이벤트를 많이 사용하고 성능이 중요하다 , 당신은 확실히 적어도하기를 원할 것입니다 고려 이렇게하지. 빈 델리게이트로 이벤트를 발생시키는 비용은 먼저 null 확인을 사용하여 이벤트를 발생시키는 비용의 약 두 배입니다.
내 컴퓨터에서 벤치 마크를 실행하는 몇 가지 수치는 다음과 같습니다.
For 50000000 iterations . . .
No null check (empty delegate attached): 530ms
With null check (no delegates attached): 249ms
With null check (with delegate attached): 452ms
그리고 다음은 이러한 수치를 얻는 데 사용한 코드입니다.
using System;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
public event EventHandler<EventArgs> EventWithDelegate = delegate { };
public event EventHandler<EventArgs> EventWithoutDelegate;
static void Main(string[] args)
{
//warm up
new Program().DoTimings(false);
//do it for real
new Program().DoTimings(true);
Console.WriteLine("Done");
Console.ReadKey();
}
private void DoTimings(bool output)
{
const int iterations = 50000000;
if (output)
{
Console.WriteLine("For {0} iterations . . .", iterations);
}
//with anonymous delegate attached to avoid null checks
var stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("No null check (empty delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
//without any delegates attached (null check required)
stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithoutAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("With null check (no delegates attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
//attach delegate
EventWithoutDelegate += delegate { };
//with delegate attached (null check still performed)
stopWatch = Stopwatch.StartNew();
for (var i = 0; i < iterations; ++i)
{
RaiseWithoutAnonDelegate();
}
stopWatch.Stop();
if (output)
{
Console.WriteLine("With null check (with delegate attached): {0}ms", stopWatch.ElapsedMilliseconds);
}
}
private void RaiseWithAnonDelegate()
{
EventWithDelegate(this, EventArgs.Empty);
}
private void RaiseWithoutAnonDelegate()
{
var handler = EventWithoutDelegate;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
}
답변
/ lot /으로 수행하는 경우 델리게이트 인스턴스의 볼륨을 줄이기 위해 재사용 할 단일 정적 / 공유 빈 델리게이트를 원할 수 있습니다. 컴파일러는 어쨌든 (정적 필드에서) 이벤트마다이 델리게이트를 캐시하므로 이벤트 정의당 하나의 델리게이트 인스턴스 일 뿐이므로 큰 절감 은 아니지만 가치가있을 수 있습니다.
물론 각 클래스의 인스턴스 별 필드는 동일한 공간을 차지합니다.
즉
internal static class Foo
{
internal static readonly EventHandler EmptyEvent = delegate { };
}
public class Bar
{
public event EventHandler SomeEvent = Foo.EmptyEvent;
}
그 외에는 괜찮아 보입니다.
답변
극단적 인 상황을 제외하고는 의미있는 성능 저하가 없습니다.
그러나 C # 6.0에서는 언어가 null 일 수있는 대리자를 호출하는 대체 구문을 제공하기 때문에이 트릭은 관련성이 떨어집니다.
delegateThatCouldBeNull?.Invoke(this, value);
위의 null 조건부 연산자 ?.
는 null 검사와 조건부 호출을 결합합니다.
답변
빈 대리자는 스레드로부터 안전하지만 null 검사는 그렇지 않다는 것을 이해합니다.
답변
나는 당신이 다음과 같은 것을하도록 유혹하기 때문에 약간 위험한 구조라고 말하고 싶습니다.
MyEvent(this, EventArgs.Empty);
클라이언트가 예외를 던지면 서버도 예외를 함께합니다.
따라서 아마도 다음과 같이 할 수 있습니다.
try
{
MyEvent(this, EventArgs.Empty);
}
catch
{
}
그러나 여러 구독자가 있고 한 구독자가 예외를 throw하면 다른 구독자는 어떻게됩니까?
이를 위해 null 검사를 수행하고 구독자 측의 예외를 삼키는 몇 가지 정적 도우미 메서드를 사용해 왔습니다 (idesign에서 가져온 것입니다).
// Usage
EventHelper.Fire(MyEvent, this, EventArgs.Empty);
public static void Fire(EventHandler del, object sender, EventArgs e)
{
UnsafeFire(del, sender, e);
}
private static void UnsafeFire(Delegate del, params object[] args)
{
if (del == null)
{
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach (Delegate sink in delegates)
{
try
{
sink.DynamicInvoke(args);
}
catch
{ }
}
}