O .NET tem um EventArgs <T> integrado?

84

Estou me preparando para criar uma classe EventArgs genérica para argumentos de evento que carregam um único argumento:

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

Antes de fazer isso, o C # tem o mesmo recurso integrado à linguagem? Parece que me lembro de ter encontrado algo assim quando o C # 2.0 foi lançado, mas agora não consigo encontrar.

Ou, dito de outra forma, eu tenho que criar minha própria classe EventArgs genérica, ou o C # fornece uma? Obrigado pela ajuda.

David Veeneman
fonte
9
Você deve ter vistoEventhandler<T>
Henk Holterman
1
Você pode ter visto EventArgs<T>no código baseado em CAB / SCSF. É bastante comum em aplicativos CAB / SCSF. Embora não seja parte do quadro, não é um EventArgs <T> implementação.
Shaun Wilson

Respostas:

66

Não. Você provavelmente estava pensando em EventHandler<T>, o que permite definir o delegado para qualquer tipo específico de EventArgs.

Eu pessoalmente não acho que EventArgs<T>seja um ajuste tão bom, no entanto. As informações usadas como uma "carga útil" nos argumentos de evento devem ser, em minha opinião, uma classe personalizada para tornar seu uso e propriedades esperadas muito claros. Usar uma classe genérica impedirá que você seja capaz de colocar nomes significativos no lugar. (O que "Dados" representam?)

Reed Copsey
fonte
50
Em aplicativos do tipo MPI centrados em dados, é bastante comum ver um EventArgs <T> no local para processamento de dados para que os desenvolvedores possam aproveitar um sistema integrado de inscrição / registro de tipo seguro (eventos e delegados .NET), já que não faz absolutamente nenhum sentido criar subclasses de EventArgs apenas para transportar dados. Se sua intenção é transportar dados, provavelmente faz mais sentido definir um EventArgs <T> que você pode criar uma subclasse para usos discretos SEM levar em consideração os dados sendo transportados. TL: DR. Esta primeira parte desta resposta é factual e precisa, a segunda parte é subjetiva / opinião.
Shaun Wilson
1
Suponho que você esteja certo ... resista à preguiça que me trouxe aqui. É como quando os desenvolvedores preguiçosos usam Tuple <,>. Coisas terríveis.
CRice 01 de
Um bom ponto, mas isso seria essencialmente a mesma coisa que a Microsoft fez quando adicionou a propriedade Tag às classes Controls. Claro, só porque MS fez isso, não significa que seja certo.
Ken Richards de
1
Concordo com o primeiro comentador, toda a segunda metade desta resposta é uma opinião subjetiva / religião-código que não foi construtiva. :(
BrainSlugs83
5
Como EventHandler <Customer> ou EventHandler <Order> transmitem menos informações do que CustomerEventHandler ou OrderEventHandler ?
Quarkly
33

Devo dizer que não entendo todos os 'puristas' aqui. ou seja, se você já tem uma classe bag definida - que tem todas as especificações, propriedades etc. - por que o hack criou uma classe extra desnecessária apenas para poder seguir o mecanismo de evento / args, estilo de assinatura? coisa é - nem tudo que está em .NET - ou está 'faltando' - é 'bom' - MS tem se 'corrigido' por anos ... Eu diria apenas vá e crie um - como eu fiz - porque eu precisava desse jeito - e me economizou muito tempo,

NSGaga-principalmente-inativo
fonte
3
Nesse caso, o ponto "purista" é deixar a intenção o mais clara possível. Em um aplicativo de produção, tenho dezenas dessas classes EventArgs. Daqui a um ano, será mais fácil entender o que fiz se seguir o conselho de Reed e criar uma classe EventArgs personalizada.
David Veeneman
9
não quis rotular ninguém :) o ponto está no 'genérico' no evento - se você só precisa de uma dúzia de eventos diferentes, ok. Mas se você não conhece a natureza do T de antemão - isso só é conhecido em tempo de execução - bem, você meio que precisa de um evento genérico. Portanto, se você tem um aplicativo que é uma boa parte para lidar com genéricos, um número arbitrário de tipos, desconhecido - você também precisa de eventos genéricos. ou seja, não há uma solução precisa para todos os problemas, a regra de ouro é apenas uma regra de ouro - e é isso que eu quis dizer com puristas, cada solução é diferente, você precisa pesar as coisas, prós e contras
NSGaga-principalmente- inativo em
2
O design não é preto e branco, então tendo a concordar com a NSGaga aqui. Às vezes, "rápido e sujo" é apropriado. A linguagem deve ser rica em recursos para se adequar à maioria dos casos de uso. Deve caber ao desenvolvedor decidir o que é apropriado. A última vez que a MS tentou forçar um design "bom" limitando a linguagem foi no WPF, quando tentou forçar o uso de XAML prejudicando gravemente a API .NET, e foi terrível.
Robear
9

Isso existe. Pelo menos agora.

Você pode encontrar DataEventArgs<TData>em alguns assemblies / namespaces diferentes da Microsoft, por exemplo, Microsoft.Practices.Prism.Events . No entanto, esses são namespaces que você pode não achar natural incluir em seu projeto, portanto, você pode usar apenas sua própria implementação.

Ulf Åkerstedt
fonte
Tenho tentado encontrar recomendações e considerações sobre quando e por que usar uma classe EventArgs genérica. Veja também: stackoverflow.com/questions/15766219/…
Ulf Åkerstedt
Aqui estão alguns aspectos a serem observados ao decidir sobre o uso de EventArgs genérico: stackoverflow.com/questions/129453/…
Ulf Åkerstedt
7

Caso você opte por não usar o Prism , mas ainda assim gostaria de tentar uma abordagem EventArgs genérica .

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

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

// Use o seguinte código de exemplo para declarar evento ObjAdded

public event EventHandler<GenericEventArgs<TargetObjType>> ObjAdded;

// Use o seguinte código de exemplo para gerar o evento ObjAdded

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

// E finalmente você pode inscrever seu evento ObjAdded

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

NÃO HÁ ARGS GENÉRICAS INCORPORADAS. Se você seguir o padrão Microsoft EventHandler, então você implementar suas EventArgs derivados como você sugeriu: public class MyStringChangedEventArgs : EventArgs { public string OldValue { get; set; } }.

NO ENTANTO - se o guia de estilo da sua equipe aceitar uma simplificação - seu projeto pode usar eventos leves, como este:

public event Action<object, string> MyStringChanged;

uso:

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

Normalmente, os projetos PoC usam a última abordagem. Em aplicativos profissionais, no entanto, esteja ciente da justificativa do policial FX # CA1009: https://msdn.microsoft.com/en-us/library/ms182133.aspx

epóxi
fonte
1

O problema com um tipo genérico é que, mesmo se DerivedType herdar de BaseType, EventArgs (DerivedType) não herdará de EventArgs (BaseType). Usar EventArgs (BaseType) evitaria, portanto, o uso posterior de uma versão derivada do tipo.

supergato
fonte
2
Isso é evidentemente falso. consulte "Covariância e Contravariância em Genéricos - Microsoft.com" msdn.microsoft.com/en-us/library/dd799517.aspx
Shaun Wilson
1
@ShaunWilson: Qual aspecto é falso? Pode-se definir uma interface covariante IEventArgs, mas as únicas classes que podem ser genéricas em relação aos parâmetros de tipo genérico são delegados que não serão alimentados Delegate.Combine. Os delegados que serão usados ​​com Delegate.Combinenão devem ser covariantes, já que se pode armazenar, por exemplo, um Action<DerivedType>em um campo do tipo Action<BaseType>, mas uma tentativa posterior Combinecom uma instância de Action<BaseType>falhará.
supercat
-4

A razão de isso não existir é porque o que acabaria acontecendo é que você implementasse isso e, em seguida, quando for preencher o T, você deve criar uma classe com propriedades inequívocas fortemente tipadas que atua como o saco de dados para o argumento do seu evento, mas no meio da implementação, você percebe que não há razão para não fazer essa classe herdar de EventArgs e chamá-la de boa.

A menos que você queira apenas uma string ou algo similarmente básico para o seu saco de dados, nesse caso provavelmente existem classes EventArgs padrão no .NET destinadas a servir a qualquer propósito simples que você esteja buscando.

Jimmy Hoffa
fonte
-1. Tenho EXATAMENTE esta situação agora, e minha carga útil seria um objeto imutável que é uma "mensagem" vinda de um ônibus ... e é veiculada em outros lugares também. O EventArgs personalizado .... cria uma classe redundante aqui. Incomum, mas o aplicativo é assim.
TomTom