2010-11-11 1 views
4

, предшествующих сообщений:Код для печати Версия для подписи событий в .NET

Event Signature in .NET — Using a Strong Typed 'Sender'?

In a C# event handler, why must the “sender” parameter be an object?


пользователи конвенции и рекомендации силы .NET от Microsoft использовать специальный шаблон для создания, повышения и обработки событий в .NET.

принципы дизайна событий http://msdn.microsoft.com/en-us/library/ms229011.aspx заявляют, что


Образец цитирования:

событие-обработчик подписи соблюдает следующие условные обозначения:

  • Возвращаемый тип Пустота.

  • Первый параметр называется отправитель и имеет тип Object. Это объект , который поднял событие.

  • Второй параметр называется е и имеет EventArgs типа или производный класс EventArgs.This является событий конкретных данных в.

  • Метод принимает ровно два параметра .


Эти конвенции говорят разработчики, что (далее) короче и более очевидным код зла:

public delegate void ConnectionEventHandler(Server sender, Connection connection); 

public partial class Server 
{ 
    protected virtual void OnClientConnected(Connection connection) 
    { 
     if (ClientConnected != null) ClientConnected(this, connection); 
    } 

    public event ConnectionEventHandler ClientConnected; 
} 

и (следующие) более и менее очевидным код хорош:

public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e); 

public class ConnectionEventArgs : EventArgs 
{ 
    public Connection Connection { get; private set; } 

    public ConnectionEventArgs(Connection connection) 
    { 
     this.Connection = connection; 
    } 
} 

public partial class Server 
{ 
    protected virtual void OnClientConnected(Connection connection) 
    { 
     if (ClientConnected != null) ClientConnected(this, new ConnectionEventArgs(connection)); 
    } 

    public event ConnectionEventHandler ClientConnected; 
} 

Хотя в этих рекомендациях не указано, почему так важно следовать этим соглашениям, заставляя разработчиков действовать как обезьяны которые не знают, почему и что они делают.

IMHO, конвенция подписи события от Microsoft для .NET плоха для вашего кода, потому что они вызывают дополнительные усилия с нулевым КПД тратиться на кодирование, кодирование, кодирование:

  1. Coding «(MyObject) отправитель» слепки (не говоря уже о 99% ситуаций, которые вообще не требуют отправителя)
  2. Кодирование, полученное из «MyEventArgs» для данных, передаваемых внутри обработчика событий.
  3. Условные обозначения кодирования (вызов «e.MyData», когда данные необходимы, а не только„данных“)

Это не так трудно сделать это усилие, но практически говоря, что мы потерять, когда не соответствующие конвенции Microsoft, за исключением того, что люди принимают вас как еретик, потому что ваш акт конфронтации к конвенциям от Microsoft verges on blasphemy :)

согласны ли вы?

+0

В качестве побочного примечания эта строка является чистым злом: 'if (ClientConnected! = Null) ClientConnected (...);'. Вы никогда не должны, * когда-либо * вызывать такие события, потому что он предполагает, что никто никогда не удалит обработчик событий из другого потока. Вы рискуете выбросить NRE здесь. Вместо этого вы должны: var h = ClientConnected; if (h! = null) h (...); '. – cdhowie

+1

К сожалению, cdhowie ваше решение для безопасности потока событий не будет работать. Пожалуйста, проверьте «неправильное решение # 2, из Руководства каркаса и MSDN» http://www.codeproject.com/Articles/37474/Threadsafe-Events.aspx (у меня не было никаких намерений делает этот поток событий безопасно, это только ради примера) Но спасибо в любом случае. – Lu4

+0

Хороший читать Lu4, спасибо. – cdhowie

ответ

3

что касается имеющего строго типизированных отправителя, я часто задавался вопросом, что я.

что касается EventArgs, я d по-прежнему рекомендую вам e промежуточный класс EventArgs, потому что вы можете захотеть добавить информацию о событиях в будущем, которую вы в настоящее время не предвидите. Если вы все время использовали определенный класс EventArgs, вы можете просто изменить сам класс и код, в котором он запускается. Если вы передадите соединение в соответствии с вашим примером, вам придется реорганизовать каждый обработчик событий.

Редактировать

Джим Mischel сделал хорошую точку в своих комментариях. Предоставляя отправителю object, мы позволяем тому же методу событий потенциально повторно использовать для обработки множества событий. Например, допустим, что сетка должна обновляться, если:

  • пользователь нажимает на кнопку «Обновить», или
  • система обнаруживает, что новая запись была загружена с сервера.

Вы могли бы сказать что-то вроде этого:

serverBus.EntryReceived += RefreshNeededHandler; 
refreshButton.Click += RefreshNeededHandler; 

... 
public void RefreshNeededHandler(object sender, EventArgs args) 
{ 
    ... 
} 

Конечно, на практике, я не очень много не было ни одного звонка для такого рода повторного использования, в то время как первое, что я, как правило, в многие, многие случаи бросают sender в тип объекта, который, как я знаю, должен быть. Если я хочу повторно использовать обработчики, подобные этому, я думаю, что было бы достаточно просто сделать два обработчика, которые оба называют один и тот же метод удобства. Для меня обработчик событий, как предполагается, должен обрабатывать определенный тип события для определенной группы объектов. Поэтому я лично не уверен, что подход object sender - лучшее соглашение.

Однако я могу представить случаи, когда это было бы очень удобно, например, если вы хотите регистрировать каждое событие, которое запускается.

+0

Нет никакой разницы, когда вы разместите информацию, которую вы не можете предвидеть. Разница между «Connection» и «EventArgs» только в их именах. – Lu4

+0

@ Lu4: Если класс Connection используется только как аргумент события, это может быть правдой. Тем не менее, я чувствую, что объекты Connection используются намного больше, чем это. Если я прав, вы могли бы когда-нибудь захотеть добавить данные, связанные с фактическим обстрелом события, но не имеющим смысла вставлять объект Connection. – StriplingWarrior

+0

Если вы идете для строго типизированного отправителя, то вы ограничиваете типы объектов, которые могут генерировать события. Например, если элементы управления пользовательского интерфейса указали, что 'Sender' является' Control', то только объекты, которые наследуют от 'Control', могут отправлять события в элементы управления пользовательского интерфейса. Это кажется мне произвольным ограничением даже для ограниченного случая элементов управления пользовательского интерфейса. –

3

Проблемы вы будете иметь:

  1. Когда вы добавляете еще один аргумент, то придется изменить событие обработчика подписи.

  2. Когда программист сначала смотрит на ваш код, ваши обработчики событий будут не будут выглядеть обработчиками событий.

Особенно последний может отпугнуть вас гораздо больше времени, чем писать 5-строчный класс.

+0

# 2 была моей первой мыслью: «Никто больше не захочет играть с (ahem -« code with »)« – Crisfole

+0

1. Нет никакой разницы между тем, как данные передаются внутри обработчика событий, будет ли это аргументом или будет ли это поле класса MyEventArgs , 2. Цель этого сообщения - изменить то, как люди думают о обработчиках событий, разработчики, которые увидят этот новый подход, будут спотыкаться в первые 5 секунд, но потом они поймут, что вся боль, которую они должны были пройти, была бессмысленной – Lu4

+2

Ах, я не знал, что ваша цель носит чисто политический характер. Я думал, ты действительно задаешь вопрос. – Stu

2

Самая большая проблема, которую я вижу в несоблюдении конвенции, заключается в том, что вы собираетесь запутать разработчиков, которые используются для обработки событий так, как это делает библиотека выполнения.Я не буду говорить, что соглашение хорошее или плохое, но это, конечно, не evil. Разработчики .NET знают и понимают, как работать с событиями, написанными в соответствии с рекомендациями Microsoft. Создание собственного механизма обработки событий в дополнение к этому может быть более эффективным во время выполнения и даже может привести к тому, что код станет чище. Но это будет по-другому, и в итоге вы получите две «обработки» обработки событий.

Моя позиция заключается в том, что лучше использовать единый стандарт, отличный от идеала (до тех пор, пока он не нарушен ужасно), чем для двух конкурирующих стандартов.

+0

Пожалуйста, опишите, как вы запутаться, когда вы увидите следующий метод: личного недействительного server_ClientConnected (отправитель сервера, подключение соединения) { ... } – Lu4

+0

@ LU4: почему он должен?Вы уже решились на это, так что хорошо бы это сделать, чтобы объяснить вещи дальше? – StriplingWarrior

+0

@ Lu4: путаница * всегда * возникает, когда есть несколько способов сделать что-то. Если программист на C#, привыкший к обработке событий, способ .NET (т.е. способ, которым обрабатывает все события в рамках всей платформы), затем сталкивается с программой, в которой некоторые события обрабатываются способом .NET, а другие обрабатываются каким-то другим способом, тогда он должен помнить, какие типы обрабатывают события таким образом. Но, эй ... это твой код. Вы можете написать его, как вам нравится. –

1

Я использовал сильно типизированные события (вместо объекта, поскольку это спасает меня от бросания), это действительно не так сложно понять, «о, смотри, что они использовали тип, который не является объектом»

Что касается eventArgs, вы должны использовать его, если объект изменяется в соответствии с ответом @StriplingWarrior.

Я не понимаю, почему разработчики запутались бы над этим?

+0

... тем более, что обработчик событий может быть сгенерирован для вас ... не могу думать о времени, когда я вручную выписал метод обработки событий. –