75 votes

+= nouveau EventHandler(Method) vs += Method

Duplicata possible :
C# : Différence entre ' += anEvent' et ' += new EventHandler(anEvent)'.

Il existe deux méthodes de base pour s'abonner à un événement :

SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;

Quelle est la différence, et quand dois-je choisir l'un plutôt que l'autre ?

Edit : Si c'est la même chose, alors pourquoi VS utilise-t-il par défaut la version longue, encombrant ainsi le code ? Cela n'a aucun sens pour moi.

46voto

Aaronaught Points 73049

Comme ma réponse initiale semblait être contestée, j'ai décidé de procéder à quelques tests, notamment en examinant le code généré. y le suivi des performances.

Tout d'abord, voici notre banc d'essai, une classe avec un délégué et une autre classe pour le consommer :

class EventProducer
{
    public void Raise()
    {
        var handler = EventRaised;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    public event EventHandler EventRaised;
}

class Counter
{
    long count = 0;
    EventProducer producer = new EventProducer();

    public void Count()
    {
        producer.EventRaised += CountEvent;
        producer.Raise();
        producer.EventRaised -= CountEvent;
    }

    public void CountWithNew()
    {
        producer.EventRaised += new EventHandler(CountEvent);
        producer.Raise();
        producer.EventRaised -= new EventHandler(CountEvent);
    }

    private void CountEvent(object sender, EventArgs e)
    {
        count++;
    }
}

La première chose à faire est de regarder l'IL généré :

.method public hidebysig instance void Count() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_0006: ldarg.0 
    L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
    L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
    L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
    L_0017: ldarg.0 
    L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
    L_0022: ldarg.0 
    L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_0028: ldarg.0 
    L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
    L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
    L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
    L_0039: ret 
}

.method public hidebysig instance void CountWithNew() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_0006: ldarg.0 
    L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
    L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
    L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
    L_0017: ldarg.0 
    L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
    L_0022: ldarg.0 
    L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
    L_0028: ldarg.0 
    L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
    L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
    L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
    L_0039: ret 
}

Il s'avère donc que, oui, ils génèrent des VA identiques. J'avais tort à l'origine. Mais c'est pas toute l'histoire . Je m'éloigne peut-être du sujet, mais je pense qu'il est important d'inclure cet élément lorsque l'on parle d'événements et de délégués :

La création et la comparaison de différents délégués ne sont pas bon marché.

Lorsque j'ai écrit ceci, je pensais que la première syntaxe permettait de couler le groupe de méthodes comme un délégué, mais il s'avère que c'est juste une conversion. Mais c'est complètement différent quand on fait sauver le délégué. Si nous ajoutons ceci au consommateur :

class Counter
{
    EventHandler savedEvent;

    public Counter()
    {
        savedEvent = CountEvent;
    }

    public void CountSaved()
    {
        producer.EventRaised += savedEvent;
        producer.Raise();
        producer.EventRaised -= savedEvent;
    }
}

Vous pouvez voir que cela a très des caractéristiques différentes, en termes de performances, des deux autres :

static void Main(string[] args)
{
    const int TestIterations = 10000000;

    TimeSpan countTime = TestCounter(c => c.Count());
    Console.WriteLine("Count: {0}", countTime);

    TimeSpan countWithNewTime = TestCounter(c => c.CountWithNew());
    Console.WriteLine("CountWithNew: {0}", countWithNewTime);

    TimeSpan countSavedTime = TestCounter(c => c.CountSaved());
    Console.WriteLine("CountSaved: {0}", countSavedTime);

    Console.ReadLine();
}

static TimeSpan TestCounter(Action<Counter> action, int iterations)
{
    var counter = new Counter();
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < TestIterations; i++)
        action(counter);
    sw.Stop();
    return sw.Elapsed;
}

Les résultats reviennent systématiquement comme quelque chose de similaire :

Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367

C'est presque un 20% différence entre l'utilisation d'un délégué sauvegardé et la création d'un nouveau délégué.

Maintenant, il est évident que tous les programmes ne vont pas ajouter et supprimer autant de délégués en si peu de temps, mais si vous écrivez des classes de bibliothèque - des classes qui peuvent être utilisées de manière imprévisible - alors vous voulez vraiment garder cette différence à l'esprit si vous devez ajouter et retirer (et j'ai écrit beaucoup de code qui fait cela, personnellement).

Donc la conclusion de ceci est, qu'écrire SomeEvent += new EventHandler(NamedMethod) compile la même chose que juste SomeEvent += NamedMethod . Mais si vous prévoyez de supprimer ce gestionnaire d'événement plus tard, vous devriez vraiment sauver le délégué . Même si le Delegate possède un code spécial qui vous permet de supprimer un délégué différent de celui que vous avez ajouté, il doit effectuer une quantité de travail non triviale pour y parvenir.

Si vous n'avez pas l'intention de sauvegarder le délégué, alors cela ne fait aucune différence - le compilateur finit par créer un nouveau délégué de toute façon.

26voto

James Points 40024

Il n'y a pas de différence du point de vue de la programmation, ils sont tous deux équivalents. Le compilateur fera à peu près ce que vous avez fait sur la première ligne avec la deuxième ligne dans les coulisses. J'opterais donc toujours pour la deuxième approche (moins de code).

Re : Votre édition

Probablement parce qu'ils pensent que c'est mieux de montrer aux développeurs le approprié manière de faire les choses plutôt que de prendre des raccourcis. Votre avis est aussi bon que le mien :)

9voto

knittl Points 64110

La deuxième forme est un sucre syntaxique introduit dans les versions ultérieures de c#. la première ligne fonctionnera dans toutes les versions cependant

7voto

Michael Buen Points 20453

Il n'y a pas de différence. Avant .NET 2.0, toutes les affectations de variables devaient être de type exact, les compilateurs n'en déduisaient pas grand chose. Afin de contourner le problème, VS 2003 émet new EventHandler autour du nom de la fonction. C'est juste mon avis. Parce que

J'ai essayé quelque chose maintenant dans VS 2008, textBox1.KeyDown += (KeyEventHandler)textBox1_KeyDown qui fonctionnent également. Je ne comprends pas pourquoi ils choisissent new EventHandler(checkBox1_CheckStateChanged) plutôt que (EventHandler)checkBox1_CheckStateChanged puis. Mais...

Comme je n'ai plus VS 2003 dans ma boîte, je ne peux pas déterminer si l'approche du casting pourrait également fonctionner sur VS 2003. Mais à ce jour, j'ai essayé de supprimer new EventHandler sur le nom de la fonction lorsque j'utilisais VS 2003 (.NET 1.1), estimant pourquoi la nécessité d'instancier ( new EventHandler ) une fonction, les délégués sont juste des pointeurs de fonction sous le capot, mais cela ne fonctionne pas.

Ce n'est qu'à partir de .NET 2.0 que le compilateur C# a commencé à inférer autant que possible.

Cet article http://blueonionsoftware.com/blog.aspx?p=aed2ae46-7548-4e5f-83c6-95e00c6f3649 a soutenu ma mémoire new EventHandler Avant les compilateurs .NET 2.0, c'était un élément obligatoire.

[EDIT]

L'article suivant traite en profondeur de l'inscription et de la désinscription des événements, en affirmant qu'il existe une différence entre les deux. button1.Click += new EventHandler(button1_Click); y button1.Click += button1_Click; mais malheureusement, je ne vois aucune différence dans le niveau d'IL :-(

http://blogs.msdn.com/abhinaba/archive/2005/08/26/456437.aspx

2voto

Thomas Points 3078

Il n'y a aucune différence, la première est juste plus spécifique dans sa définition.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X