Question

Je ne parviens pas à comprendre et à mettre en œuvre les événements en C # en utilisant des délocalisations. Je suis habitué à la manière de faire de Java:

  1. Définir une interface pour un type d'écouteur qui contiendrait un certain nombre de définitions de méthodes
  2. Définissez la classe d'adaptateur pour cette interface afin de simplifier les choses si tous les événements définis dans un écouteur ne m'intéressent pas
  3. Définissez les méthodes Add, Remove and Get [] dans la classe qui déclenche les événements
  4. Définissez des méthodes de tir protégées pour effectuer le travail fastidieux de parcourir en boucle la liste des écouteurs ajoutés et d'appeler la bonne méthode

Ceci, je le comprends (et aime!) - Je sais que je pourrais le faire exactement de la même manière en c #, mais il semble qu’un nouveau système (meilleur?) est en place pour c #. Après avoir lu d'innombrables tutoriels expliquant l'utilisation de délégués et d'événements en c #, je ne suis toujours pas près de vraiment comprendre ce qui se passe: S

En bref, pour les méthodes suivantes, comment implémenter le système d'événements en c #:

void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);

^ Les méthodes ci-dessus proviennent d'une application Java que j'ai déjà créée et que j'essaie de transférer vers c #.

Merci beaucoup!

Était-ce utile?

La solution

Vous créez quatre événements et des méthodes pour les générer, ainsi qu'une nouvelle classe basée sur EventArgs pour indiquer l'erreur:

public class ExceptionEventArgs : EventArgs
{
    private readonly Exception error;

    public ExceptionEventArgs(Exception error)
    {
         this.error = error;
    }

    public Error
    {
         get { return error; }
    }
}

public class Computer
{
    public event EventHandler Started = delegate{};
    public event EventHandler Stopped = delegate{};
    public event EventHandler Reset = delegate{};
    public event EventHandler<ExceptionEventArgs> Error = delegate{};

    protected void OnStarted()
    {
        Started(this, EventArgs.Empty);
    }

    protected void OnStopped()
    {
        Stopped(this, EventArgs.Empty);
    }

    protected void OnReset()
    {
        Reset(this, EventArgs.Empty);
    }

    protected void OnError(Exception e)
    {
        Error(this, new ExceptionEventArgs(e));
    }
}

Les classes seraient ensuite abonnées à l'événement à l'aide d'une méthode ou d'une fonction anonyme:

someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{ 
    Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");

Quelques points à noter à propos de ce qui précède:

  • Les méthodes OnXXX sont protégées afin que les classes dérivées puissent déclencher les événements. Ce n'est pas toujours nécessaire, faites-le comme bon vous semble.
  • L'élément delegate {} de chaque déclaration d'événement est juste une astuce pour éviter de devoir effectuer une vérification nulle. Il souscrit un gestionnaire d'événement no-op à chaque événement
  • Les déclarations d'événements sont des événements de type champ . Ce qui est en train d'être créé est à la fois une variable et un événement. Dans la classe, vous voyez la variable. en dehors de la classe, vous voyez l'événement.

Voir mon événements / délégués article pour en savoir plus sur les événements.

Autres conseils

Vous devrez définir un seul délégué pour ce

public delegate void ComputerEvent(object sender, ComputerEventArgs e);

ComputerEventArgs serait défini comme suit:

public class ComputerEventArgs : EventArgs
{
    // TODO wrap in properties
    public Computer computer;
    public Exception error;

    public ComputerEventArgs(Computer aComputer, Exception anError)
    {
        computer = aComputer;
        error = anError;
    }

    public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
    {
    }
}

La classe qui déclenche les événements aurait:

public YourClass
{
    ...
    public event ComputerEvent ComputerStarted;
    public event ComputerEvent ComputerStopped;
    public event ComputerEvent ComputerReset;
    public event ComputerEvent ComputerError;
    ...
}

Voici comment vous affectez des gestionnaires aux événements:

YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);

Votre gestionnaire est:

private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
   // do your thing.
}

La principale différence est qu'en C # les événements ne sont pas basés sur une interface. Au lieu de cela, l'éditeur d'événements déclare le délégué que vous pouvez considérer comme un pointeur de fonction (bien que ce ne soit pas exactement pareil :-)). L'abonné implémente ensuite le prototype d'événement en tant que méthode habituelle et ajoute une nouvelle instance du délégué à la chaîne de gestionnaires d'événements de l'éditeur. En savoir plus sur les délégués et événements .

Vous pouvez également lire une brève comparaison d'événements C # et d'événements Java ici .

Tout d’abord, il existe une signature de méthode standard dans .Net qui est généralement utilisée pour les événements. Les langages permettent d'utiliser n'importe quelle sorte de signature de méthode pour les événements. Certains experts pensent que la convention est défectueuse (je suis plutôt d'accord), mais c'est ce qu'elle est et je la suivrai pour cet exemple.

  1. Créez une classe qui contiendra les paramètres de l'événement (dérivés de EventArgs).
public class ComputerEventArgs : EventArgs 
{
  Computer computer; 
  // constructor, properties, etc.
}
  1. Créez un événement public sur la classe devant déclencher l'événement.
    class ComputerEventGenerator  // I picked a terrible name BTW.
    {
      public event EventHandler<ComputerEventArgs> ComputerStarted;
      public event EventHandler<ComputerEventArgs> ComputerStopped;
      public event EventHandler<ComputerEventArgs> ComputerReset;
    ...
    }
  1. Appelez les événements.
    class ComputerEventGenerator
    {
    ...
      private void OnComputerStarted(Computer computer) 
      {
        EventHandler<ComputerEventArgs> temp = ComputerStarted;
        if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static
      }
     }
  1. Associez un gestionnaire à l'événement.
    void OnLoad()
    {
      ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator();
      computerEventGenerator.ComputerStarted += new  EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted);
    }
  1. Créez le gestionnaire que vous venez d'attacher (principalement en appuyant sur la touche de tabulation dans VS).
    private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args)
    {
      if (args.Computer.Name == "HAL9000")
         ShutItDownNow(args.Computer);
    }
  1. N'oubliez pas de détacher le gestionnaire lorsque vous avez terminé. (Oublier de faire cela est la plus grande source de fuites de mémoire en C #!)
    void OnClose()
    {
      ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted;
    }

Et c'est tout!

EDIT: Honnêtement, je n'arrive pas à comprendre pourquoi mes points numérotés apparaissent tous sous la forme "1." Je déteste les ordinateurs.

il y a plusieurs façons de faire ce que vous voulez. La manière la plus directe serait de définir des délégués pour chaque événement de la classe d'hébergement, par exemple

.
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
    if (ComputerStarted != null)
    {
        ComputerStarted.Invoke(computer);
    }
}
protected void someMethod()
{
    //...
    computer.Started = true;  //or whatever
    OnComputerStarted(computer);
    //...
}

n'importe quel objet peut "écouter" cet événement simplement par:

Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
    this.ComputerStartedHandler);

protected void ComputerStartedHandler(Computer computer)
{
    //do something
}

La "méthode standard recommandée" consiste à définir une sous-classe de EventArgs qui contiendra la ou les valeurs Ordinateur (et ancien / nouvel état et exception), ce qui réduira le nombre de délégués à 4. Dans ce cas, ce serait une solution plus propre, en particulier. avec un Enum pour l’état de l’ordinateur en cas de développement ultérieur. Mais la technique de base reste la même:

  • le délégué définit la signature / interface du gestionnaire d'événement / écouteur
  • le membre de données d'événement est une liste de 'écouteurs'

les écouteurs sont supprimés en utilisant la syntaxe - = au lieu de + =

En c #, les événements sont des délégués. Ils se comportent de la même manière qu’un pointeur de fonction en C / C ++ mais sont des classes réelles dérivées de System.Delegate.

Dans ce cas, créez une classe EventArgs personnalisée pour transmettre l'objet Ordinateur.

public class ComputerEventArgs : EventArgs
{
  private Computer _computer;

  public ComputerEventArgs(Computer computer) {
    _computer = computer;
  }

  public Computer Computer { get { return _computer; } }
}

Exposez ensuite les événements du producteur:

public class ComputerEventProducer
{
  public event EventHandler<ComputerEventArgs> Started;
  public event EventHandler<ComputerEventArgs> Stopped;
  public event EventHandler<ComputerEventArgs> Reset;
  public event EventHandler<ComputerEventArgs> Error;

  /*
  // Invokes the Started event */
  private void OnStarted(Computer computer) {
    if( Started != null ) {
      Started(this, new ComputerEventArgs(computer));
    }
  }

  // Add OnStopped, OnReset and OnError

}

Le consommateur des événements lie ensuite une fonction de gestionnaire à chaque événement du consommateur.

public class ComputerEventConsumer
{
  public void ComputerEventConsumer(ComputerEventProducer producer) {
    producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
    // Add other event handlers
  }

  private void ComputerStarted(object sender, ComputerEventArgs e) {
  }
}

Lorsque ComputerEventProducer appelle OnStarted, l'événement Started est appelé, qui appelle à son tour la méthode ComputerEventConsumer.ComputerStarted.

Le délégué déclare une signature de fonction. Lorsqu'il est utilisé en tant qu'événement dans une classe, il agit également en tant que collection de cibles d'appel répertoriées. La syntaxe + = et - = d’un événement permet d’ajouter une cible à la liste.

Étant donné les délégués suivants utilisés comme événements:

// arguments for events
public class ComputerEventArgs : EventArgs
{
    public Computer Computer { get; set; }
}

public class ComputerErrorEventArgs : ComputerEventArgs
{
    public Exception Error  { get; set; }
}

// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);

public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);

// component that raises events
public class Thing
{
    public event ComputerEventHandler Started;
    public event ComputerEventHandler Stopped;
    public event ComputerEventHandler Reset;
    public event ComputerErrorEventHandler Error;
}

Vous souhaitez vous abonner à ces événements avec les éléments suivants:

class Program
{
    static void Main(string[] args)
    {
        var thing = new Thing();
        thing.Started += thing_Started;
    }

    static void thing_Started(object sender, ComputerEventArgs e)
    {
        throw new NotImplementedException();
    }
}

Bien que les arguments puissent être n'importe quoi, l'expéditeur de l'objet et EventArgs e sont une convention utilisée de manière très cohérente. Le + = thing_started créera d’abord une instance du délégué pointant sur la méthode cible, puis l’ajoutera à l’événement.

Sur le composant lui-même, vous ajouterez généralement des méthodes pour déclencher les événements:

public class Thing
{
    public event ComputerEventHandler Started;

    public void OnStarted(Computer computer)
    {
        if (Started != null)
            Started(this, new ComputerEventArgs {Computer = computer});
    }
}

Vous devez tester la valeur null si aucun délégué n'a été ajouté à l'événement. Lorsque vous appelez la méthode, tous les délégués ajoutés seront appelés. C’est pourquoi, pour les événements, le type de retour est void (il n’existe pas de valeur de retour unique). Par conséquent, pour renvoyer des informations, vous disposez de propriétés sur les EventArgs que les gestionnaires d’événements modifieraient.

Un autre raffinement consisterait à utiliser le délégué générique EventHandler plutôt que de déclarer un délégué concret pour chaque type d'argument.

public class Thing
{
    public event EventHandler<ComputerEventArgs> Started;
    public event EventHandler<ComputerEventArgs> Stopped;
    public event EventHandler<ComputerEventArgs> Reset;
    public event EventHandler<ComputerErrorEventArgs> Error;
}

Merci beaucoup à tous pour vos réponses! Enfin, je commence à comprendre ce qui se passe. Juste une chose; Il semble que si chaque événement avait un nombre / type d'arguments différent, je devrais créer une classe différente :: EventArgs pour le traiter:

public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);

Cela nécessiterait trois classes pour faire face aux événements!? (Eh bien, deux personnalisées et une utilisant la classe EventArgs.Empty par défaut)

Salut!

Ok, dernière précision!: C’est donc le mieux que je puisse faire en termes de code pour mettre en œuvre ces événements?

   public class Computer {

        public event EventHandler Started;

        public event EventHandler Stopped;

        public event EventHandler Reset;

        public event EventHandler<BreakPointEvent> BreakPointHit;

        public event EventHandler<ExceptionEvent> Error;

        public Computer() {
            Started = delegate { };
            Stopped = delegate { };
            Reset = delegate { };
            BreakPointHit = delegate { };
            Error = delegate { };
        }

        protected void OnStarted() {
            Started(this, EventArgs.Empty);
        }

        protected void OnStopped() {
            Stopped(this, EventArgs.Empty);
        }

        protected void OnReset() {
            Reset(this, EventArgs.Empty);
        }

        protected void OnBreakPointHit(int breakPoint) {
            BreakPointHit(this, new BreakPointEvent(breakPoint));
        }

        protected void OnError(System.Exception exception) {
            Error(this, new ExceptionEvent(exception));
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top