Есть ли в .NET встроенный EventArgs <T>?

85

Я собираюсь создать общий класс EventArgs для аргументов событий, которые содержат один аргумент:

public class EventArg<T> : EventArgs
{
    // Property variable
    private readonly T p_EventData;

    // Constructor
    public EventArg(T data)
    {
        p_EventData = data;
    }

    // Property for EventArgs argument
    public T Data
    {
        get { return p_EventData; }
    }
}

Прежде чем я это сделаю, есть ли в C # такая же функция, встроенная в язык? Я припоминаю, что сталкивался с чем-то подобным, когда вышел C # 2.0, но теперь я не могу его найти.

Или, другими словами, нужно ли мне создавать свой собственный общий класс EventArgs, или он есть в C #? Спасибо за вашу помощь.

Дэвид Винеман
источник
9
Вы, наверное, виделиEventhandler<T>
Хенк Холтерман
1
Возможно, вы видели EventArgs<T>код на основе CAB / SCSF. Это довольно часто встречается в приложениях CAB / SCSF. Хотя это не входит в рамки, то есть Объект EventArgs <T> реализация.
Шон Уилсон,

Ответы:

68

Нет. Вы, наверное, думали о том EventHandler<T>, что позволяет вам определять делегата для любого конкретного типа EventArgs.

Лично я не считаю, что EventArgs<T>это так хорошо подходит. Информация, используемая в качестве «полезной нагрузки» в аргументах событий, должна, на мой взгляд, быть настраиваемым классом, чтобы сделать ее использование и ожидаемые свойства очень понятными. Использование универсального класса не позволит вам поставить на место значимые имена. (Что представляют собой «Данные»?)

Рид Копси
источник
52
В ориентированных на данные, MPI-подобных приложениях довольно часто можно увидеть EventArgs <T> для обработки данных, чтобы разработчики могли использовать встроенную систему подписки / регистрации с типобезопасностью (.NET Events и Delegates), поскольку нет абсолютно никакого смысла создавать подклассы EventArgs только для передачи данных. Если ваше намерение состоит в том, чтобы транспортировать данные, возможно, имеет смысл определить EventArgs <T>, который вы можете подклассифицировать для дискретного использования БЕЗ учета передаваемых данных. TL: DR Эта первая часть этого ответа фактическая и точная, вторая часть субъективна / мнение.
Шон Уилсон,
1
Полагаю, вы правы ... сопротивляйтесь лени, которая меня сюда привела. Это похоже на то, когда ленивые разработчики используют Tuple <,>. Ужасная штука.
CRice 01
Хороший момент, но по сути это будет то же самое, что и Microsoft, когда они добавили свойство Tag в классы Controls. Конечно, просто потому, что это сделал М.С., не исправляет.
Ken Richards
1
Я согласен с первым комментатором, вся вторая половина этого ответа является субъективным мнением / кодексом религии, которое не было конструктивным. :(
BrainSlugs83 06
6
Как EventHandler <Customer> или EventHandler <Order> передает меньше информации, чем CustomerEventHandler или OrderEventHandler ?
Quarkly,
33

Я должен сказать, что не понимаю всех здесь «пуристов». т.е. если у вас уже есть определенный класс сумки - который имеет все особенности, свойства и т. д. - почему хак создает один лишний ненужный класс, просто чтобы иметь возможность следовать механизму событий / аргументов, стилю подписи? Дело в том, что не все, что есть в .NET - или «отсутствует» в этом отношении - это «хорошо» - MS «исправляла» себя годами ... Я бы сказал, просто пойди и создай его - как я - потому что мне это было нужно именно так - и сэкономил мне много времени,

NSGaga-в основном-неактивный
источник
3
В этом случае цель «пуристов» - сделать намерение максимально ясным. В производственном приложении у меня есть десятки классов EventArgs. Через год мне будет легче понять, что я сделал, если я последую совету Рида и создам собственный класс EventArgs.
Дэвид Винеман
9
не имел в виду кого-то маркировать :) точка находится в «общем» в событии - если вам просто нужна дюжина разных событий, хорошо. Но если вы не знаете заранее природу T - это известно только во время выполнения - что ж, вам нужно какое-то общее событие. Итак, если у вас есть приложение, которое хорошо работает с дженериками, произвольным числом типов, неизвестным - тогда вам также нужны общие события. или, например, нет четкого решения для каждой проблемы, эмпирическое правило - это просто эмпирическое правило - и это то, что я имел в виду под пуристами, каждое решение отличается, вам нужно взвесить все, за и против
NSGaga-в основном- неактивен
2
Дизайн не является черно-белым, поэтому я склонен согласиться с NSGaga здесь. Иногда уместно «быстро и грязно». Язык должен быть многофункциональным, чтобы соответствовать большинству случаев использования. Разработчик должен решить, что подходит. В последний раз MS пыталась навязать «хороший» дизайн, ограничивая язык, в WPF, когда они пытались принудительно использовать XAML, серьезно затрудняя работу .NET API, и это было ужасно.
Robear
9

Он существует. По крайней мере, сейчас.

Вы можете найти DataEventArgs<TData>в некоторых других сборках / пространствах имен Microsoft, например Microsoft.Practices.Prism.Events . Однако это пространства имен, которые вы не можете найти естественным для включения в свой проект, поэтому вы можете просто использовать свою собственную реализацию.

Ульф Окерштедт
источник
Я пытался найти рекомендации и соображения относительно того, когда и зачем использовать такой общий класс EventArgs. См. Также: stackoverflow.com/questions/15766219/…
Ульф Окерстедт 09
Вот некоторые аспекты, которых следует остерегаться при выборе универсального EventArgs: stackoverflow.com/questions/129453/…
Ульф Окерстедт 03
7

Если вы решили не использовать Prism , но все же хотели бы попробовать общий подход EventArgs .

public class GenericEventArgs<T> : EventArgs
{
    public T EventData { get; private set; }

    public GenericEventArgs(T EventData)
    {
        this.EventData = EventData;
    }
}

// Используйте следующий пример кода для объявления события ObjAdded

public event EventHandler<GenericEventArgs<TargetObjType>> ObjAdded;

// Используйте следующий пример кода, чтобы вызвать событие ObjAdded

private void OnObjAdded(TargetObjType TargetObj)
{
    if (ObjAdded!= null)
    {
        ObjAdded.Invoke(this, new GenericEventArgs<TargetObjType>(TargetObj));
    }
}

// И, наконец, вы можете подписаться на событие ObjAdded

SubscriberObj.ObjAdded +=  (object sender, GenericEventArgs<TargetObjType> e) =>
{
    // Here you can explore your e.EventData properties
};
Хулио Нобре
источник
3

НЕТ ВСТРОЕННЫХ ОБЩИХ АРГОВ. Если вы будете следовать Microsoft EventHandler шаблон, то вы реализуете ваши полученные EventArgs , как вы предложили: public class MyStringChangedEventArgs : EventArgs { public string OldValue { get; set; } }.

ОДНАКО - если ваше руководство по командному стилю допускает упрощение - ваш проект может использовать облегченные события, например:

public event Action<object, string> MyStringChanged;

Применение :

// How to rise
private void OnMyStringChanged(string e)
{
    Action<object, string> handler = MyStringChanged;    // thread safeness
    if (handler != null)
    {
        handler(this, e);
    }
}

// How to handle
myObject.MyStringChanged += (sender, e) => Console.WriteLine(e);

Обычно в PoC-проектах используется второй подход. Однако в профессиональных приложениях следует помнить об оправдании FX Cop № CA1009: https://msdn.microsoft.com/en-us/library/ms182133.aspx

эпоксидная смола
источник
1

Проблема с универсальным типом заключается в том, что даже если DerivedType наследуется от BaseType, EventArgs (DerivedType) не будет наследовать от EventArgs (BaseType). Таким образом, использование EventArgs (BaseType) предотвратит последующее использование производной версии типа.

суперкар
источник
2
Это явная ложь. см. «Ковариация и контравариантность в обобщенных версиях
Шон Уилсон,
1
@ShaunWilson: Что неверно? Можно определить ковариантный интерфейс IEventArgs, но единственными классами, которые могут быть универсальными в отношении параметров универсального типа, являются делегаты, которым не будут передаваться данные Delegate.Combine. Делегаты, которые будут использоваться с, Delegate.Combineне должны быть ковариантными, поскольку можно сохранить, например, Action<DerivedType>в поле типа Action<BaseType>, но более поздняя попытка сделать Combineэто с экземпляром Action<BaseType>не удастся.
supercat
-4

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

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

Джимми Хоффа
источник
-1. Сейчас у меня ТОЧНО эта ситуация, и моей полезной нагрузкой будет неизменяемый объект, который является «сообщением», пришедшим из автобуса ... и также используется в других местах. Пользовательский EventArgs .... создает здесь избыточный класс. Необычно, но приложение такое.
TomTom