ser*_*g10 82 c# events delegates coding-style idioms
我已经看到了一些关于这个习语的提及(包括SO):
// Deliberately empty subscriber
public event EventHandler AskQuestion = delegate {};
Run Code Online (Sandbox Code Playgroud)
好处很明显 - 它避免了在提升事件之前检查null的必要性.
但是,我很想知道是否有任何缺点. 例如,它是否被广泛使用并且足够透明以至于不会引起维护问题?空事件用户呼叫是否有明显的性能影响?
Jud*_*ngo 46
为什么不使用扩展方法来缓解这两个问题,而不是诱导性能开销:
public static void Raise(this EventHandler handler, object sender, EventArgs e)
{
if(handler != null)
{
handler(sender, e);
}
}
Run Code Online (Sandbox Code Playgroud)
一旦定义,您再也不必再执行另一个空事件检查:
// Works, even for null events.
MyButtonClick.Raise(this, EventArgs.Empty);
Run Code Online (Sandbox Code Playgroud)
Ken*_*art 42
对于大量使用事件且性能至关重要的系统,您肯定希望至少考虑不这样做.使用空委托引发事件的成本大约是使用空检查首先引发事件的两倍.
以下是我的机器上运行基准测试的一些数字:
For 50000000 iterations . . .
No null check (empty delegate attached): 530ms
With null check (no delegates attached): 249ms
With null check (with delegate attached): 452ms
Run Code Online (Sandbox Code Playgroud)
以下是我用来获取这些数字的代码:
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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Mau*_*ice 36
唯一的缺点是,当您调用额外的空委托时,会有非常轻微的性能损失.除此之外,没有维护惩罚或其他缺点.
如果您正在使用/ lot /,您可能希望拥有一个可重复使用的静态/共享空委托,只需减少委托实例的数量.请注意,编译器无论如何都会在每个事件中缓存此委托(在静态字段中),因此每个事件定义只有一个委托实例,因此它不是一个巨大的节省 - 但可能值得.
当然,每个类中的每个实例字段仍将占用相同的空间.
即
internal static class Foo
{
internal static readonly EventHandler EmptyEvent = delegate { };
}
public class Bar
{
public event EventHandler SomeEvent = Foo.EmptyEvent;
}
Run Code Online (Sandbox Code Playgroud)
除此之外,它看起来很好.
归档时间: |
|
查看次数: |
11380 次 |
最近记录: |