Quelles sont les différences entre les délégués et les événements?

quelles sont les différences entre les délégués et un événement? Ne pas contenir des références à des fonctions qui peuvent être exécutées?

273
demandé sur 5StringRyan 2008-08-27 03:06:31

10 réponses

Un Événement déclaration ajoute une couche d'abstraction et de protection sur le délégué instance. Cette protection empêche les clients du délégué de réinitialiser le délégué et sa liste d'invocations et ne permet que d'ajouter ou de supprimer des cibles de la liste d'invocations.

249
répondu mmcdole 2008-08-26 23:16:18

en plus des propriétés syntaxiques et opérationnelles, il y a aussi une différence sémantique.

les délégués sont, conceptuellement, des modèles de fonctions; c'est-à-dire qu'ils expriment un contrat auquel une fonction doit adhérer pour être considérée du "type" du délégué.

Événements représentent ... ainsi, les événements. Ils sont destinés à alerter quelqu'un quand quelque chose arrive et oui, ils adhèrent à une définition de délégué mais ils ne sont pas les mêmes chose.

même s'ils étaient exactement la même chose (syntaxiquement et dans le code IL), il restera toujours la différence sémantique. En général, je préfère avoir deux noms différents pour deux concepts différents, même s'ils sont implémentés de la même manière (ce qui ne veut pas dire que j'aime avoir le même code deux fois).

90
répondu Jorge Córdoba 2009-09-26 20:14:56

pour comprendre les différences, vous pouvez regarder ces 2 Exemples

exemple avec des délégués (dans ce cas, une Action - qui est une sorte de délégué qui ne rend pas une valeur)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

Pour utiliser le délégué, vous devriez faire quelque chose comme ceci:

Animal animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

Ce code fonctionne bien, mais vous pourriez avoir quelques points faibles.

Par exemple, si j'écris ceci:

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

avec la dernière ligne de code, j'ai dépassé les comportements précédents juste avec un manquant + (j'ai utilisé = au lieu de += )

un autre point faible est que chaque classe qui utilise votre classe Animal peut soulever RaiseEvent simplement l'appelant animal.RaiseEvent() .

pour éviter ces points faibles, vous pouvez utiliser events dans c#.

votre classe D'Animal va changer en par ici:

public class ArgsSpecial : EventArgs
{
    public ArgsSpecial (string val)
    {
        Operation=val;
    }

    public string Operation {get; set;}
} 

public class Animal
{
    // Empty delegate. In this way you are sure that value is always != null 
    // because no one outside of the class can change it.
    public event EventHandler<ArgsSpecial> Run = delegate{} 

    public void RaiseEvent()
    {  
         Run(this, new ArgsSpecial("Run faster"));
    }
}

pour appeler les événements

 Animal animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

différences:

  1. vous n'utilisez pas un bien public mais un champ public (en utilisant events, le compilateur protège vos champs de l'accès indésirable)
  2. Les événements
  3. ne peuvent pas être assignés directement. Dans ce cas, il ne donnera pas lieu à l'erreur précédente que j'ai montré en outrepassant le comportement.
  4. personne en dehors de votre classe peut soulever l'événement.
  5. Événements peuvent être inclus dans une déclaration d'interface, alors qu'un champ ne peut pas

Notes:

EventHandler est déclaré délégué suivant:

public delegate void EventHandler (object sender, EventArgs e)

il prend un expéditeur (de type D'objet) et des arguments d'événement. L'expéditeur est nul s'il provient de méthodes statiques.

cet exemple, qui utilise EventHandler<ArgsSpecial> , peut aussi être écrit en utilisant EventHandler à la place.

Consultez ici de la documentation sur EventHandler

80
répondu faby 2017-01-19 10:44:08

C'est un vieux post, mais si quelqu'un trébuche sur elle, comme je l'ai fait ici est un autre bon lien pour consulter.. http://csharpindepth.com/Articles/Chapter2/Events.aspx

brièvement, les take away de l'article - événements sont encapsulation sur les délégués. Citation de l'article -

" supposez que les événements n'existent pas en tant que concept dans C#/.NET. Comment une autre classe s'abonner à un événement?

Trois options:

  1. public delegatevariable

  2. variable déléguée adossée à une propriété

  3. délégué variable avec AddXXXHandler et RemoveXXXHandler méthodes

L'Option 1 est clairement horrible, pour toutes les raisons normales nous abhorrons les variables publiques.

Option 2 est mieux, mais permet aux abonnés de passer effectivement uns les autres, il serait trop facile d'écrire someInstance.MyEvent = eventHandler; qui remplacerait plutôt tout gestionnaire d'événements existant que l'ajout d'un nouveau. En outre, vous devez toujours écrire l' propriété.

Option 3 est essentiellement ce que les événements vous donnent, mais avec une garantie convention (générée par le compilateur et appuyée par des options supplémentaires dans le IL) et un " free" mise en œuvre si vous êtes satisfait de la sémantique ce que les évènements de terrain vous donnent. S'abonner ou se désabonner à événements est encapsulé sans laisser l'arbitraire l'accès à la liste de gestionnaires d'événements, et les langues peuvent rendre les choses plus simples en fournissant syntaxe pour la déclaration et l'abonnement."

35
répondu vibhu 2014-03-12 19:09:08

vous pouvez également utiliser des événements dans les déclarations d'interface, pas pour les délégués.

6
répondu Paul Hill 2010-08-25 14:37:21

NOTE: Si vous avez accès à C# 5.0 déchaîné , lisez la section "Limites de L'utilisation courante des délégués" au chapitre 18 intitulé "événements" pour mieux comprendre les différences entre les deux.


cela m'aide toujours d'avoir un exemple simple et concret. En voici une pour la communauté. Tout d'abord, je vous montre comment vous pouvez utiliser les délégués seuls pour faire ce que les événements font pour nous. Puis je montre comment la même solution fonctionnerait avec un exemple de EventHandler . Et puis j'explique pourquoi nous ne voulons pas faire ce que j'explique dans le premier exemple. Ce billet a été inspiré par un article de John Skeet.

exemple 1: Utilisation du délégué public

supposons que j'ai une application WinForms avec une seule boîte déroulante. La liste déroulante est lié à un List<Person> . Lorsque la personne possède des propriétés D'identification, de nom, de surnom, de coiffure. Sur le formulaire principal est un contrôle utilisateur personnalisé qui montre les propriétés de cette personne. Lorsque quelqu'un sélectionne une personne dans la liste déroulante, les étiquettes de la mise à jour du contrôle de l'utilisateur indiquent les propriétés de la personne sélectionnée.

enter image description here

voilà comment ça marche. Nous avons trois fichiers qui nous aident à mettre cela ensemble:

  • Mediator.cs -- statique classe contient les délégués
  • Formulaire1.cs -- formulaire principal
  • La fenêtre detravail.cs -- contrôle de l'utilisateur affiche tous les détails

voici le code correspondant pour chacune des classes:

class Mediator
{
    public delegate void PersonChangedDelegate(Person p); //delegate type definition
    public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this.
    public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes.
    {
        if (PersonChangedDel != null)
        {
            PersonChangedDel(p);
        }
    }
}

voici le contrôle de l'utilisateur:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.PersonChangedDel += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(Person p)
    {
        BindData(p);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

enfin, nous avons le code suivant dans notre Formulaire1.cs. Ici, nous faisons appel Àpersonchanged, qui appelle tout code souscrit au délégué.

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}

Ok. Donc c'est comme ça que vous obtiendriez ce fonctionnement sans utiliser les événements et en utilisant seulement les délégués . Nous avons juste mis un délégué public dans une classe -- vous pouvez la rendre statique ou simple, ou autre. Grand.

mais, Mais, mais, nous ne voulons pas faire ce que je viens de décrire ci-dessus. Parce que les champs publics sont mauvais pour beaucoup, beaucoup de raisons. Alors, quelles sont nos options? Comme le décrit John Skeet, voici nos options:

  1. Un public variable déléguée (c'est ce que nous venons de faire ci-dessus. ne pas le faire. je vous l'ai dit ci-dessus pourquoi c'est mauvais)
  2. mettre le délégué dans une propriété avec un get/set (le problème ici est que les abonnés pourraient passer outre l'un l'autre -- donc nous pourrions souscrire un tas de méthodes au délégué et puis nous pourrions accidentellement dire PersonChangedDel = null , effaçant tous les autres abonnements. L'autre problème qui demeure ici est que, puisque les utilisateurs ont accès à l'délégué, ils peuvent invoquer les cibles dans la liste d'invocation -- nous ne voulons pas que les utilisateurs externes ayant accès pour élever nos événements.
  3. Un délégué variable avec AddXXXHandler et RemoveXXXHandler méthodes

Cette troisième option est essentiellement ce qu'un événement nous donne. Quand nous déclarons un EventHandler, il nous donne accès à un délégué-pas publiquement, pas comme une propriété, mais comme cette chose que nous appelons un événement qui vient d'Ajouter/Supprimer des accesseurs.

voyons à quoi ressemble le même programme, mais en utilisant maintenant un événement à la place du délégué public (j'ai aussi changé notre Médiateur en un singleton):

exemple 2: avec EventHandler au lieu d'un délégué public

Médiateur:

class Mediator
{

    private static readonly Mediator _Instance = new Mediator();

    private Mediator() { }

    public static Mediator GetInstance()
    {
        return _Instance;
    }

    public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate.

    public void OnPersonChanged(object sender, Person p)
    {
        var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>;
        if (personChangedDelegate != null)
        {
            personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p });
        }
    }
}

notez que si vous F12 sur le EventHandler, il vous montrera que la définition est juste un délégué générique avec l'objet supplémentaire "sender":

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

Le Contrôle De L'Utilisateur:

public partial class DetailView : UserControl
{
    public DetailView()
    {
        InitializeComponent();
        Mediator.GetInstance().PersonChanged += DetailView_PersonChanged;
    }

    void DetailView_PersonChanged(object sender, PersonChangedEventArgs e)
    {
        BindData(e.Person);
    }

    public void BindData(Person p)
    {
        lblPersonHairColor.Text = p.HairColor;
        lblPersonId.Text = p.IdPerson.ToString();
        lblPersonName.Text = p.Name;
        lblPersonNickName.Text = p.NickName;

    }
}

enfin, voici le Formulaire1.code cs:

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
        Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem);
}

parce que L'EventHandler veut et EventArgs comme paramètre, j'ai créé cette classe avec une seule propriété:

class PersonChangedEventArgs
{
    public Person Person { get; set; }
}

avec un peu de chance, cela vous montre un peu pourquoi nous avons des événements et comment ils sont différents-mais fonctionnellement les mêmes-que les délégués.

6
répondu Trevor 2017-05-23 11:33:24

quel grand malentendu entre les événements et les délégués!!! Un délégué spécifie un TYPE (tel qu'un class , ou un interface does), alors qu'un événement est juste une sorte de membre (tel que des champs, des propriétés, etc.). Et, comme tout autre type de membre, un événement a aussi un type. Pourtant, dans le cas d'un événement, le type de l'événement doit être spécifiée par un délégué. Par exemple, vous ne pouvez pas déclarer un événement d'un type défini par une interface.

En conclusion, nous pouvons faire l'Observation suivante : le type d'un événement doit être défini par un délégué . Il s'agit de la relation principale entre un événement et un délégué et est décrite dans la section II.18 événements définissant de ECMA-335 (CLI) Partitions I à VI :

dans l'usage courant, le TypeSpec (s'il est présent) identifie un délégué dont la signature correspond à la arguments passés à la méthode de feu de l'événement.

Toutefois, ce fait n'implique PAS qu'un événement utilise un support délégué champ . En vérité, un événement peut utiliser un champ de stockage de données différents type de structure de votre choix. Si vous implémentez un événement explicitement dans C#, vous êtes libre de choisir la façon dont vous stockez les event handlers (notez que event handlers sont des instances de la type de l'événement , qui à son tour est obligatoirement un type de délégué - - - du précédent Observation ). Mais, vous pouvez stocker ces gestionnaires d'événements (qui sont des instances déléguées) dans une structure de données telle qu'un List ou un Dictionary ou tout autre, ou même dans un champ de délégué de soutien. Mais n'oubliez pas qu'il n'est pas obligatoire d'utiliser un champ délégué.

6
répondu Miguel Gamboa 2015-09-01 09:43:03

un événement dans .net est une combinaison désignée d'une méthode Add et D'une méthode Remove, qui tous deux s'attendent à un type particulier de délégué. À la fois C# et vb.net peut auto-générer du code pour ajouter et supprimer des méthodes qui définiront un délégué pour détenir les abonnements d'événement, et Ajouter/Supprimer le passé dans delegagte à/de ce délégué d'abonnement. VB.net va également générer automatiquement du code (avec la déclaration RaiseEvent) pour invoquer la liste d'abonnement si et seulement si elle est non vide; pour une raison quelconque, C# ne génère pas ce dernier.

notez que bien qu'il soit courant de gérer les abonnements à des événements en utilisant un délégué de multidiffusion, ce n'est pas le seul moyen de le faire. D'un point de vue public, un abonné éventuel à un événement a besoin de savoir comment faire savoir à un objet qu'il veut recevoir des événements, mais il n'a pas besoin de savoir quel mécanisme l'éditeur utilisera pour soulever les événements. Notez également que si celui qui a défini la structure de données d'événements dans .net apparemment j'ai pensé qu'il devrait y avoir un moyen public de les élever, ni C# ni vb.net utilise cette fonctionnalité.

4
répondu supercat 2011-08-27 17:07:07

pour définir l'événement de manière simple:

l'Événement est un de RÉFÉRENCE à un délégué avec deux restrictions

  1. ne peut être invoqué directement
  2. Les valeurs
  3. ne peuvent pas être attribuées directement (E. g eventObj = delegateMethod)

au-dessus de deux sont les points faibles pour les délégués et il est abordé en cas. Exemple de code complet pour montrer la différence dans fiddler est ici https://dotnetfiddle.net/5iR3fB .

basculer le commentaire entre L'événement et le code délégué et client qui invoque / attribue des valeurs à déléguer pour comprendre la différence

voici le code en ligne.

 /*
This is working program in Visual Studio.  It is not running in fiddler because of infinite loop in code.
This code demonstrates the difference between event and delegate
        Event is an delegate reference with two restrictions for increased protection

            1. Cannot be invoked directly
            2. Cannot assign value to delegate reference directly

Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines
*/

public class RoomTemperatureController
{
    private int _roomTemperature = 25;//Default/Starting room Temperature
    private bool _isAirConditionTurnedOn = false;//Default AC is Off
    private bool _isHeatTurnedOn = false;//Default Heat is Off
    private bool _tempSimulator = false;
    public  delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof)
    // public  OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 
    public  event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), 

    public RoomTemperatureController()
    {
        WhenRoomTemperatureChange += InternalRoomTemperatuerHandler;
    }
    private void InternalRoomTemperatuerHandler(int roomTemp)
    {
        System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed");
    }

    //User cannot directly asign values to delegate (e.g. roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error)
    public bool TurnRoomTeperatureSimulator
    {
        set
        {
            _tempSimulator = value;
            if (value)
            {
                SimulateRoomTemperature(); //Turn on Simulator              
            }
        }
        get { return _tempSimulator; }
    }
    public void TurnAirCondition(bool val)
    {
        _isAirConditionTurnedOn = val;
        _isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }
    public void TurnHeat(bool val)
    {
        _isHeatTurnedOn = val;
        _isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary)
        System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn);
        System.Console.WriteLine("Heat :" + _isHeatTurnedOn);

    }

    public async void SimulateRoomTemperature()
    {
        while (_tempSimulator)
        {
            if (_isAirConditionTurnedOn)
                _roomTemperature--;//Decrease Room Temperature if AC is turned On
            if (_isHeatTurnedOn)
                _roomTemperature++;//Decrease Room Temperature if AC is turned On
            System.Console.WriteLine("Temperature :" + _roomTemperature);
            if (WhenRoomTemperatureChange != null)
                WhenRoomTemperatureChange(_roomTemperature);
            System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status
        }
    }

}

public class MySweetHome
{
    RoomTemperatureController roomController = null;
    public MySweetHome()
    {
        roomController = new RoomTemperatureController();
        roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp;
        //roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible.
        //roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event
        roomController.SimulateRoomTemperature();
        System.Threading.Thread.Sleep(5000);
        roomController.TurnAirCondition (true);
        roomController.TurnRoomTeperatureSimulator = true;

    }
    public void TurnHeatOrACBasedOnTemp(int temp)
    {
        if (temp >= 30)
            roomController.TurnAirCondition(true);
        if (temp <= 15)
            roomController.TurnHeat(true);

    }
    public static void Main(string []args)
    {
        MySweetHome home = new MySweetHome();
    }


}
3
répondu Venkatesh Muniyandi 2016-09-13 00:09:39

Covariance et Contravariance plus de flexibilité pour le délégué des objets. D'autre part, un événement n'a pas de tels concepts.

  • Covariance " vous permet d'attribuer une méthode au délégué lorsque le le type de retour de la méthode est une classe dérivée de la classe qui spécifie le type de retour du délégué.
  • Contravariance permet d'attribuer une méthode au délégué lorsque le type de paramètre de la méthode est une classe de base de la classe qui est spécifié comme paramètre du délégué.
0
répondu vivek nuna 2018-08-29 06:35:59