由於我的原始答案似乎存在一些爭議,因此我決定做一些測試,包括查看生成的代碼和監控性能。
首先,這裏是我們的測試牀,一個代表和其他類的類來使用它:
class EventProducer
{
public void Raise()
{
var handler = EventRaised;
if (handler != null)
handler(this, EventArgs.Empty);
}
public event EventHandler EventRaised;
}
class Counter
{
long count = 0;
EventProducer producer = new EventProducer();
public void Count()
{
producer.EventRaised += CountEvent;
producer.Raise();
producer.EventRaised -= CountEvent;
}
public void CountWithNew()
{
producer.EventRaised += new EventHandler(CountEvent);
producer.Raise();
producer.EventRaised -= new EventHandler(CountEvent);
}
private void CountEvent(object sender, EventArgs e)
{
count++;
}
}
第一件事要做的就是看看產生IL:
.method public hidebysig instance void Count() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
.method public hidebysig instance void CountWithNew() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
所以事實證明,是的,這些產生了相同的IL。我原來錯了。但這是不是整個故事。這可能是我在這裏討論的話題,但我認爲在談論事件和代表時包括這一點很重要:
創建和比較不同代表並不便宜。
當我寫這篇文章時,我想第一個語法能夠將方法組轉換爲委託,但事實證明它只是一個轉換。但當你真的保存委託時,它是完全不同的。如果加上這給消費者:
class Counter
{
EventHandler savedEvent;
public Counter()
{
savedEvent = CountEvent;
}
public void CountSaved()
{
producer.EventRaised += savedEvent;
producer.Raise();
producer.EventRaised -= savedEvent;
}
}
你可以看到,這有非常不同的特點,性能,明智的,從其他兩個:
static void Main(string[] args)
{
const int TestIterations = 10000000;
TimeSpan countTime = TestCounter(c => c.Count());
Console.WriteLine("Count: {0}", countTime);
TimeSpan countWithNewTime = TestCounter(c => c.CountWithNew());
Console.WriteLine("CountWithNew: {0}", countWithNewTime);
TimeSpan countSavedTime = TestCounter(c => c.CountSaved());
Console.WriteLine("CountSaved: {0}", countSavedTime);
Console.ReadLine();
}
static TimeSpan TestCounter(Action<Counter> action, int iterations)
{
var counter = new Counter();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < TestIterations; i++)
action(counter);
sw.Stop();
return sw.Elapsed;
}
結果一致回來的東西類似於:
Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367
,使用保存d時,幾乎是一個20%差異elegate與創建一個新的。
現在顯然不是每個程序都會在這麼短的時間內添加和刪除這麼多的代表,但是如果你正在編寫圖書館類 - 可能以你無法預測的方式使用的類 - 那麼你真的如果您需要添加並刪除事件(並且我已經編寫了大量代碼,可以親自執行此操作),請記住這一區別。
因此,這個結論是,寫作SomeEvent += new EventHandler(NamedMethod)
編譯爲只有SomeEvent += NamedMethod
相同的東西。但如果你打算刪除事件處理程序後,你真的應該保存委託。儘管Delegate
類有一些特殊代碼,允許您從添加的代碼中刪除不同於指定代理的代理,但它必須執行一些不重要的工作來解決此問題。
如果你不打算保存委託,那麼它沒有什麼區別 - 編譯器最終會創建一個新的委託。
代碼生成器經常混亂(使用長限定名稱而不是使用指令)。它們旨在簡化生成並避免錯誤,而不是爲了可讀性。 – 2010-05-01 14:45:39
我正在專門討論代碼段,它並未被自動化代碼生成器使用。 – mafu 2010-05-01 16:34:06
我也是如此。長形式不太可能產生歧義。 – 2010-05-01 17:52:12