Я цілком усвідомлюю, що те, що я пропоную, не відповідає вказівкам .NET, і, отже, це, мабуть, погана ідея лише з цієї причини. Однак я хотів би розглянути це з двох можливих перспектив:
(1) Чи варто розглянути можливість використання цього для моєї власної розробки, що становить 100% для внутрішніх цілей.
(2) Чи це концепція, яку розробники рамок могли розглянути як змінити чи оновити?
Я думаю про використання підпису події, який використовує сильний набраний "відправник", а не вводити його як "об'єкт", що є поточним шаблоном дизайну .NET. Тобто замість використання стандартного підпису події, який виглядає приблизно так:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Я розглядаю можливість використання підпису події, який використовує сильно набраний параметр 'відправник' таким чином:
Спочатку визначте "StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Це не все так, що відрізняється від Action <TSender, TEventArgs>, але, використовуючи StrongTypedEventHandler
, ми застосовуємо, що TEventArgs походить від System.EventArgs
.
Далі, як приклад, ми можемо використовувати StrongTypedEventHandler у класі видавництва наступним чином:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
Вищевказана домовленість дозволить підписникам використовувати обробник подій із сильним типом, який не потребує кастингу:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
Я цілком усвідомлюю, що це порушується зі стандартною схемою обробки подій .NET; однак, майте на увазі, що протиріччя дозволить передплатнику використовувати традиційний підпис для обробки події за бажанням:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
Тобто, якщо обробнику подій потрібно було підписатись на події з різних (або, можливо, невідомих) типів об'єктів, обробник може ввести параметр "відправник" як "об'єкт", щоб обробити всю широту потенційних об'єктів відправника.
Окрім порушення конвенції (що я не сприймаю з легкістю, повірте), я не можу придумати жодних недоліків цього.
Тут можуть виникнути деякі проблеми дотримання CLS. Це працює в Visual Basic .NET 2008 на 100% добре (я тестував), але я вважаю, що старіші версії Visual Basic .NET до 2005 року не мають делегатської коваріації та противаріантності. [Редагувати: я з цього часу перевірив це, і це підтверджено: VB.NET 2005 і нижче не можуть це впоратися, але VB.NET 2008 на 100% добре. Див. "Редагувати №2", нижче.] Можуть бути інші мови .NET, які також мають проблему з цим, я не можу бути впевнений.
Але я не бачу себе розробляти для будь-якої іншої мови, крім C # або Visual Basic .NET, і не проти обмежувати його на C # і VB.NET для .NET Framework 3.0 і вище. (Я не міг уявити собі повернутися до 2.0 в цей момент, якщо чесно.)
Хтось ще може подумати про проблему з цим? Або це просто розривається з умовою настільки, що змушує живіт людей обертатися?
Ось кілька пов’язаних посилань, які я знайшов:
(1) Правила проектування подій [MSDN 3.5]
(3) Шаблон підпису події в .net [StackOverflow 2008]
Мене цікавить чиясь думка і думка кожного з цього приводу ...
Спасибі заздалегідь,
Майк
Редагувати №1: Це відповідь на повідомлення Томмі Карлєра :
Ось повний робочий приклад, який показує, що з таким підходом можуть існувати як обробники подій із сильним типом, так і поточні стандартні обробники подій, які використовують параметр 'відправник об’єкта'. Ви можете скопіювати і вставити в код і дати йому пробіг:
namespace csScrap.GenericEventHandling
{
class PublisherEventArgs : EventArgs
{
// ...
}
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs());
}
}
}
class StrongTypedSubscriber
{
public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
}
}
class TraditionalSubscriber
{
public void SomeEventHandler(object sender, PublisherEventArgs e)
{
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
}
}
class Tester
{
public static void Main()
{
Publisher publisher = new Publisher();
StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();
publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;
publisher.OnSomeEvent();
}
}
}
Редагувати №2: Це відповідь на заяву Ендрю Зайця щодо коваріації та протиріччя та те, як воно застосовується тут. У делегатів на мові C # так довго існували коваріантність та протиріччя, що вона просто відчуває себе "внутрішньою", але це не так. Це може бути навіть щось, що включено в CLR, я не знаю, але Visual Basic .NET не отримав можливості коваріації та протиріччя для своїх делегатів до .NET Framework 3.0 (VB.NET 2008). І як результат, Visual Basic.NET для .NET 2.0 та новіших версій не зможе використовувати цей підхід.
Наприклад, наведений вище приклад можна перекласти у VB.NET наступним чином:
Namespace GenericEventHandling
Class PublisherEventArgs
Inherits EventArgs
' ...
' ...
End Class
<SerializableAttribute()> _
Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
(ByVal sender As TSender, ByVal e As TEventArgs)
Class Publisher
Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)
Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me, New PublisherEventArgs)
End Sub
End Class
Class StrongTypedSubscriber
Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
End Sub
End Class
Class TraditionalSubscriber
Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
End Sub
End Class
Class Tester
Public Shared Sub Main()
Dim publisher As Publisher = New Publisher
Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber
AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler
publisher.OnSomeEvent()
End Sub
End Class
End Namespace
VB.NET 2008 може виконати 100% штраф. Але зараз я перевірив його на VB.NET 2005, просто напевно, і він не компілюється, зазначаючи:
Метод 'Public Sub SomeEventHandler (відправник як об’єкт, e як vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' не має такої ж підпису, як делегат 'Delegate Sub StrongTypedEventHandler (TSender, TEventArgs As System.EventArgs) (відправник видавець, видавець видавець '
В основному, делегати є інваріантними у версіях VB.NET 2005 та нижче. Я насправді думав над цією ідеєю пару років тому, але нездатність VB.NET впоратися з цим непокоїла мене ... Але я тепер твердо перейшов до C #, і VB.NET тепер може з цим впоратися, так що, отже, отже це повідомлення.
Правка: оновлення №3
Гаразд, я вже досить довго використовую це. Це дійсно приємна система. Я вирішив назвати свій "StrongTypedEventHandler" "GenericEventHandler", визначений так:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Окрім цього перейменування, я реалізував його саме так, як було сказано вище.
Це дійсно подорож за правилом CA1009 FxCop, де зазначено:
"За умовою, події .NET мають два параметри, які задають відправника події та дані події. Підписи обробника події повинні відповідати цій формі: недійсний MyEventHandler (відправник об'єкта, EventArgs e). Параметр" відправник "завжди має тип System.Object, навіть якщо можливо використовувати більш конкретний тип. Параметр "е" завжди має тип System.EventArgs. Події, які не надають дані про події, повинні використовувати тип делегата System.EventHandler. Обробники подій повертають недійсні, щоб вони могли надсилати Кожна подія для кількох цільових методів. Будь-яке значення, повернене ціллю, втрачається після першого дзвінка. "
Звичайно, ми все це знаємо і все одно порушуємо правила. (Усі обробники подій можуть використовувати стандартний 'Відправник об’єкта' у своєму підписі, якщо бажано в будь-якому випадку - це безперебійна зміна.)
Таким чином, використання a SuppressMessageAttribute
робить трюк:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
Я сподіваюся, що такий підхід стане еталоном в якийсь момент майбутнього. Це дійсно дуже добре працює.
Дякую за всі ваші думки, хлопці, я дуже вдячний ...
Майк
oh hi this my hom work solve it plz :code dump:
питань розміром з твіттом, а питання, з якого ми дізнаємось .
EventHandler<,>
чим GenericEventHandler<,>
. В EventHandler<>
BCL вже є загальний, який названий просто EventHandler. Тож EventHandler - більш поширене ім'я, і делегати підтримують перевантаження типів