+= новый EventHandler (метод) vs += метод [дубликат]
Возможные Дубликаты:
C#: разница между '+ = anEvent ' и ' + = new EventHandler (anEvent)’
есть два основных способа подписаться на событие:
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
какая разница, и когда я должен выбрать один над другим?
Edit: если это то же самое, то почему VS по умолчанию использует длинную версию, загромождая код? Для меня это вообще не имеет смысла.
5 ответов:
поскольку, казалось, был некоторый спор по поводу моего первоначального ответа, я решил сделать несколько тестов, в том числе глядя на сгенерированный код и мониторинг производительности.
прежде всего, вот наш тестовый стенд, класс с делегатом и другой класс, чтобы использовать его:
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на 20% разница при использовании сохраненного делегата против создания нового.
теперь очевидно, что не каждая программа будет добавлять и удаление этого количества делегатов за такое небольшое количество времени, но если вы пишете библиотечные классы-классы, которые могут использоваться способами, которые вы не можете предсказать, - тогда вы действительно хотите сохранить эту разницу в виду, если вам когда-нибудь понадобится добавить и удалить событий (и я написал много кода, который делает это, лично).
Итак, вывод это писать!--5--> компилируется в то же самое, что просто
SomeEvent += NamedMethod. Но если вы планируете удалить это событие обработчик позже,вы действительно должны сохранить делегат. ХотяDelegateкласс имеет некоторый специальный код, который позволяет удалить ссылочно-другой делегат от того, который вы добавили, он должен выполнить нетривиальный объем работы, чтобы снять это.если вы не собираетесь сохранять делегат, то это не имеет никакого значения - компилятор в конечном итоге создает новый делегат в любом случае.
нет никакой разницы с точки зрения программирования, они эквивалентны друг другу. Компилятор будет делать то, что вы сделали на первой линии со второй линии за кулисами. Поэтому я всегда выбирал бы второй подход (меньше кода).
Re: Ваше Редактирование
наверное, потому что они чувствуют себя лучше, чтобы показать разработчикам правильный способ делать вещи, а не ярлыки. Ваша догадка так же хороша, как и моя :)
вторая форма-синтаксический сахар, введенный в более поздних версиях c#. первая строка будет работать в каждой версии, хотя
нет никакой разницы. До .NET 2.0 все назначения переменных должны иметь точный тип, компиляторы тогда не выводили много. Так как, чтобы сделать работу-вокруг, против 2003 испускают
new EventHandlerвокруг имени функции. Это только мое предположение. Потому что..Я пробовал что-то сейчас в VS 2008,
textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown, это тоже работает. Это озадачивает меня, почему они выбираютnew EventHandler(checkBox1_CheckStateChanged), а не(EventHandler)checkBox1_CheckStateChangedзатем. Но...поскольку у меня больше нет VS 2003 в моей коробке, я не могу определить, если кастинг подход может также работать на VS 2003. Но afaict, я попытался удалить
new EventHandlerна имя функции, когда я использовал VS 2003 (.NET 1.1), считая, почему необходимо создать экземпляр (new EventHandler) функция, делегаты просто указатель функции под капотом, но это не работает.только с .NET 2.0 и далее компилятор C# начал выводить как можно больше.
эта статья http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 поддерживается моя память о
new EventHandlerдо компиляторов .NET 2.0 это был принудительный[EDIT]
в следующей статье подробно описывается подписка / неподписание событий, предполагая, что есть разница между
button1.Click += new EventHandler(button1_Click);иbutton1.Click += button1_Click;, но, к сожалению, я не вижу никакой разницы в уровне IL, хотя: - (http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx
Comments