Czy .NET ma wbudowaną funkcję EventArgs <T>?

84

Przygotowuję się do utworzenia ogólnej klasy EventArgs dla argumentów zdarzeń, które zawierają pojedynczy argument:

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; }
    }
}

Zanim to zrobię, czy C # ma tę samą funkcję wbudowaną w język? Wydaje mi się, że napotkałem coś takiego, gdy pojawił się C # 2.0, ale teraz nie mogę go znaleźć.

Albo inaczej, czy muszę utworzyć własną klasę ogólną EventArgs, czy też C # ją udostępnia? Dzięki za pomoc.

David Veeneman
źródło
9
Być może widzieliścieEventhandler<T>
Henk Holterman
1
Być może widziałeś EventArgs<T>w kodzie opartym na CAB / SCSF. Jest to dość powszechne w aplikacjach CAB / SCSF. Choć nie jest to częścią ram, tam jest an EventArgs <T> realizacja.
Shaun Wilson

Odpowiedzi:

66

Nie. Prawdopodobnie EventHandler<T>myślałeś o tym , co pozwala zdefiniować delegata dla dowolnego konkretnego typu EventArgs.

Osobiście uważam jednak, że nie EventArgs<T>jest to tak dobre dopasowanie. Informacje używane jako „ładunek” w argumentach zdarzeń powinny być, moim zdaniem, niestandardową klasą, aby jej użycie i oczekiwane właściwości były bardzo jasne. Użycie klasy ogólnej uniemożliwi Ci wstawianie znaczących nazw w miejsce. (Co oznaczają „Dane”?)

Reed Copsey
źródło
50
W aplikacjach zorientowanych na dane, podobnych do MPI, dość często spotyka się EventArgs <T> w miejscu przetwarzania danych, dzięki czemu programiści mogą wykorzystać wbudowany, bezpieczny system subskrypcji / rejestracji (.NET Events and Delegates), ponieważ absolutnie nie ma sensu tworzenie podklas EventArgs tylko do transportu danych. Jeśli Twoim zamiarem jest transport danych, prawdopodobnie bardziej sensowne jest zdefiniowanie EventArgs <T>, możesz utworzyć podklasę dla dyskretnych zastosowań BEZ zwracania uwagi na transportowane dane. TL: DR Ta pierwsza część tej odpowiedzi jest zgodna z faktami i dokładna, druga część jest subiektywna / opinia.
Shaun Wilson
1
Przypuszczam, że masz rację ... oprzyj się lenistwu, które mnie tu sprowadziło. To tak, jakby leniwi programiści używali Tuple <,>. Okropne rzeczy.
CRice
Dobra uwaga, ale zasadniczo oznaczałoby to to samo, co Microsoft, gdy dodali właściwość Tag do klas Controls. Oczywiście, po prostu stwardnienie rozsiane to zrobiło, nie naprawia tego.
Ken Richards,
1
Zgadzam się z pierwszym komentatorem, cała druga połowa tej odpowiedzi to subiektywna opinia / kod-religia, która nie była konstruktywna. :(
BrainSlugs83
5
W jaki sposób EventHandler <Customer> lub EventHandler <Order> przekazuje mniej informacji niż CustomerEventHandler lub OrderEventHandler ?
Quarkly
33

Muszę powiedzieć, że nie rozumiem tu wszystkich „purystów”. tzn. jeśli masz już zdefiniowaną klasę worka - która ma wszystkie szczegóły, właściwości itp. - dlaczego hack tworzy jedną dodatkową niepotrzebną klasę tylko po to, aby móc śledzić mechanizm zdarzeń / argumentów, styl sygnatury? rzecz w tym, że - nie wszystko, co jest w .NET - lub czego „brakuje” - jest „dobre” - MS „poprawia się” od lat ... Powiedziałbym, po prostu idź i stwórz taki - tak jak ja - bo tak właśnie tego potrzebowałem - i zaoszczędziłem dużo czasu,

NSGaga-głównie-nieaktywny
źródło
3
W tym przypadku celem „purystów” jest jak najbardziej jasne przedstawienie intencji. W aplikacji produkcyjnej mam dziesiątki klas EventArgs. Za rok łatwiej będzie zrozumieć, co zrobiłem, jeśli postąpię zgodnie z radą Reeda i utworzę niestandardową klasę EventArgs.
David Veeneman
9
nie chciałem nikogo oznaczać :) chodzi o to, co jest „ogólne” w przypadku - jeśli potrzebujesz tylko kilkunastu różnych wydarzeń, ok. Ale jeśli nie znasz wcześniej natury litery T - jest to znane tylko w czasie wykonywania - cóż, potrzebujesz zdarzenia ogólnego. Tak więc, jeśli masz aplikację, która dobrze radzi sobie z typami generycznymi, dowolną liczbą typów, nieznanych - potrzebujesz również zdarzeń ogólnych. lub tj. nie ma prostego rozwiązania dla każdego problemu,
praktyczna zasada to tylko praktyczna
2
Projekt nie jest czarno-biały, więc zgadzam się tutaj z NSGagą. Czasami „szybko i brudno” jest właściwe. Język powinien być bogaty w funkcje, aby pasował do większości przypadków użycia. To programista powinien zdecydować, co jest odpowiednie. Ostatnim razem, gdy MS próbowało wymusić „dobry” projekt, ograniczając język, był WPF, kiedy próbowali wymusić użycie XAML przez poważne utrudnienia dla API .NET i było to straszne.
Robear
9

Ona istnieje. Przynajmniej teraz.

Możesz znaleźć DataEventArgs<TData>w różnych zestawach / przestrzeniach nazw Microsoft , na przykład Microsoft.Practices.Prism.Events . Są to jednak przestrzenie nazw, których umieszczanie w projekcie może nie być naturalne, więc możesz po prostu użyć własnej implementacji.

Ulf Åkerstedt
źródło
Próbowałem znaleźć zalecenia i uwagi dotyczące tego, kiedy i dlaczego używać takiej ogólnej klasy EventArgs. Zobacz też: stackoverflow.com/questions/15766219/…
Ulf Åkerstedt
Oto kilka aspektów, na które należy uważać, decydując się na użycie ogólnego EventArgs: stackoverflow.com/questions/129453/ ...
Ulf Åkerstedt
7

Jeśli zdecydujesz się nie używać Prism , ale nadal chciałbyś wypróbować ogólne podejście EventArgs .

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

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

// Użyj następującego przykładowego kodu, aby zadeklarować zdarzenie ObjAdded

public event EventHandler<GenericEventArgs<TargetObjType>> ObjAdded;

// Użyj następującego przykładowego kodu, aby zgłosić zdarzenie ObjAdded

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

// I na koniec możesz zasubskrybować swoje zdarzenie ObjAdded

SubscriberObj.ObjAdded +=  (object sender, GenericEventArgs<TargetObjType> e) =>
{
    // Here you can explore your e.EventData properties
};
Julio Nobre
źródło
3

NIE MA WBUDOWANYCH OGÓLNYCH ARG. Jeśli zastosujemy Microsoft EventHandler wzór, a następnie wdrożyć swoje EventArgs wywodzące się jak sugeruje: public class MyStringChangedEventArgs : EventArgs { public string OldValue { get; set; } }.

JEDNAK - jeśli przewodnik stylu Twojego zespołu akceptuje uproszczenie - Twój projekt może korzystać z lekkich wydarzeń, takich jak to:

public event Action<object, string> MyStringChanged;

stosowanie :

// 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);

Zwykle projekty PoC wykorzystują to drugie podejście. W profesjonalnych zastosowaniach należy jednak pamiętać o uzasadnieniu policjanta FX # CA1009: https://msdn.microsoft.com/en-us/library/ms182133.aspx

epoks
źródło
1

Problem z typem ogólnym polega na tym, że nawet jeśli DerivedType dziedziczy po BaseType, EventArgs (DerivedType) nie będzie dziedziczyć po EventArgs (BaseType). Użycie EventArgs (BaseType) uniemożliwiłoby w ten sposób późniejsze użycie pochodnej wersji typu.

supercat
źródło
2
To jest ewidentnie fałszywe. zobacz „Kowariancja i kontrawariancja w produktach generycznych - Microsoft.com” msdn.microsoft.com/en-us/library/dd799517.aspx
Shaun Wilson,
1
@ShaunWilson: Jaki aspekt jest fałszywy? Można by zdefiniować kowariantny interfejs IEventArgs, ale jedynymi klasami, które mogą być generyczne w odniesieniu do parametrów typu ogólnego, są delegaci, do których nie będą przekazywane Delegate.Combine. Delegaty, które będą używane z, Delegate.Combinenie powinny być kowariantne, ponieważ można zapisać np. A Action<DerivedType>w polu typu Action<BaseType>, ale późniejsza próba wykonania Combinetego z instancją Action<BaseType>zakończy się niepowodzeniem.
supercat
-4

Powodem, dla którego to nie istnieje, jest to, że to, co by się stało, to zaimplementowanie tego, a następnie kiedy idziesz do wypełnienia T, powinieneś utworzyć klasę z silnie wpisanymi jednoznacznymi właściwościami, która działa jako zbiór danych dla twojego zdarzenia arg, ale w połowie implementacji zdajesz sobie sprawę, że nie ma powodu, dla którego nie chcesz po prostu dziedziczyć tej klasy po EventArgs i nazwać ją dobrą.

Chyba że potrzebujesz tylko ciągu znaków lub czegoś podobnie podstawowego dla swojego zbioru danych, w takim przypadku prawdopodobnie istnieją standardowe klasy EventArgs w .NET, które mają służyć dowolnemu prostemu celowi.

Jimmy Hoffa
źródło
-1. Mam teraz DOKŁADNIE tę ostrożność, a mój ładunek byłby niezmiennym obiektem, który jest „wiadomością” pochodzącą z autobusu ... i jest wysyłany także w innych miejscach. Niestandardowa EventArgs .... tworzy tutaj nadmiarową klasę. Niezwykłe, ale aplikacja taka jest.
TomTom