2017-05-23 2 views
1

Tenir compte le code suivant:usine générique pour les sous-classes concrètes

public interface IEventHandler<in T> where T : IEvent 
{ 
    void Execute(T @event); 
} 

Pour ces gestionnaires je mets en œuvre certains gestionnaires sous-classes concrètes:

public class SomeEventHandler : IEventHandler<SomeEvent> 
{ 
    public void Execute(SomeEvent @event) { /* ... */ } 
} 

Maintenant, j'ai une usine pour récupérer le gestionnaire correspondant d'un Événement:

public class EventHandlerFactory : IEventHandlerFactory 
{ 
    public IEventHandler<T> Create<T>(T @event) where T : IEvent 
    { 
     // What to do in here? 
    } 
} 

J'ai essayé de vérifier le type de l'événement, puis de retourner le corre gestionnaire corres, mais bien sûr, le système de type nie:

if(@event is SomeEvent) 
{ 
    return (IEventHandler<T>) new SomeEventHandler(); 
} 

Je ne ferais pas cela si hacky, mais je me demande comment permettre le code de faire cette interface avec le Create<T> possible.

EDIT: Quand je boucle à travers un enumarable d'événements T est traité comme IEvent de sorte que le système de type jetterai une exception:

var events = IEnumarable<IEvent>() { /* ... */ }; 

foreach (var @event in events) 
{ 
    var eventHandler = eventHandlerFactory.Create(@event); 
} 

Le Create(T @event) -method lèveront une exception, car T est un IEvent et pas le type concret. Je pourrais le résoudre en utilisant (dynamic) @event mais ce n'est pas ce que je veux vraiment faire.

+0

[Veuillez ne pas placer de balises dans les titres des questions] (https://stackoverflow.com/help/tagging) – Liam

+0

Copie possible de [Créer une instance de type générique?] (Https://stackoverflow.com/questions/731452/ create-instance-of-ge neric-type) – Liam

+0

Ce qui crée ceci - 'events = IEnumarable ()' - que contient le code commenté? D'où viennent les 'IEvent's – Alex

Répondre

1

Modèle d'usine classique.
Ce que j'aime faire des choses comme celle-ci est la suivante:

public class EventHandlerFactory : IEventHandlerFactory 
{ 
    private readonly Dictionary<Type, Type> _eventHandlers = new Dictionary<Type, Type>(); 

    public EventHandlerFactory() 
    { 
     //add a mapping between the type, and the handler 
     //note - this could be done with reflection to automate this 
     _eventHandlers.Add(typeof(SomeEvent), typeof(SomeEventHandler)); 
    } 

    public IEventHandler<T> Create<T>(T @event) where T : IEvent 
    { 
     var handler = _eventHandlers[typeof(T)]; 

     if (handler != null) 
     { 
      //now use Activator.CreateInstance to instantiate the type 
      return (IEventHandler<T>)Activator.CreateInstance(handler); 
     } 

     throw new Exception("Handler not found"); 
    } 
} 
+0

Voici comment je le ferais, avec peut-être quelques modifications mineures. Par exemple, cela peut être meilleur en tant que classe statique. Envisagez également de lancer une exception plutôt que de renvoyer une valeur nulle. – DavidG

+1

Oui, je suis d'accord avec le bit d'exception - Avoir édité – Alex

+0

Je ne l'ai pas rendu explicitement statique car je le gère normalement via un conteneur ioc - Je fais de mon instantiation de 'IEventHandlerFactory' une seule instance – Alex

0

Autre solution:

Si vous voulez éviter la réflexion ou vos implémentations IEventHandler n'ont pas des constructeurs par défaut:

public interface IEventHandler 
{ 
} 

public interface IEventHandler<in T> : IEventHandler where T : IEvent 
{ 
    void Execute(T @event); 
} 

public class EventHandlerFactory 
{ 
    private static readonly Dictionary<Type, Func<IEventHandler>> _eventHandlers = new Dictionary<Type, Func<IEventHandler>> 
    { 
     { typeof(SomeEvent),() => new SomeEventHandler() } 
    }; 

    public IEventHandler<T> Create<T>(T @event) where T : IEvent 
    { 
     Func<IEventHandler> handler; 
     if (_eventHandlers.TryGetValue(typeof(T), out handler)) 
     { 
      return (IEventHandler<T>)handler(); 
     } 

     throw new Exception("Handler not found"); 
    } 
}