2016-04-02 5 views
0

Ich arbeite an einem kleinen Unity-Projekt mit C#.Wie abonniere ich Ereignisse, die in mehreren Instanzen einer Klasse ausgelöst wurden?

Ich habe eine Klasse UnitManager, die eine Liste der Instanzen der Klasse Unit enthält. Ich möchte ein Ereignis auslösen, wenn sich eine Eigenschaft (z. B. Gesundheit) innerhalb einer Instanz von Unit ändert. Ich verwende auch die UnitManager, um die eventHandlers zu speichern. Zum Beispiel habe ich einen UI-Manager, der das Ereignis in der UnitManager abonniert.

Das Problem, das ich habe, ist, dass ich nicht wollen, dass die Unit Klasse über die UnitManager Klasse. Der Code, den ich gerade habe (unten) funktioniert, aber ich denke, das würde als gekoppelten Code betrachtet werden?

public class Unit 
{ 
    private int health; 
    public int Health { 
     get { return health; } 
     set 
     { 
      health = value; 
      UnitManager.Instance.OnHealthChanged(this); //Unit shouldn't call UnitManager, right? 
     } 
    } 
} 

public class UnitManager 
{ 

    protected List<Unit> units = new List<Unit>(); 

    public delegate void UnitHealthChangedEventHandler(object source, UnitEventArgs args); 
    public event UnitHealthChangedEventHandler HealthChanged; 

    public virtual void OnHealthChanged(Unit _unit) 
    { 
     if (HealthChanged != null) 
     { 
      HealthChanged(this, new UnitEventArgs() { unit = _unit }); 
     } 
    } 
} 

public class UIManager 
{ 
    void Start() 
    { 
     UnitManager.Instance.HealthChanged += OnUnitHealthChanged; 
    } 
} 

Nach einem MSDN sample sollte der Ereignis-Handler in das Unit (das Ereignisse Sender) gespeichert werden. Der Unterschied ist, dass das Beispiel auf MSDN nur eine Instanz von "Counter" hat, aber mein Code hat mehrere Instanzen. Dies würde bedeuten, dass ich alle Instanzen der Einheit durchlaufen muss und sie dann abonnieren muss. Ich fürchte, das würde ein Performance-Problem werden. (Vor allem, da ich an mehreren Stellen in meinem Code das gleiche Problem habe)

Auf meine Frage zusammenfassen: was den besten Weg ist (in Bezug auf OOP/lose Kopplung) ein Eventhandler Griff Ereignisse zu lassen, die in mehreren Instanzen einer Klasse angehoben?

Antwort

1

Werden die Listener beim Start erstellt oder können sie von einem Container für Abhängigkeitsinjektionen aufgelöst werden?

Ein anderer Ansatz, der die Kopplung reduziert und die Bindung von Ereignishandlern konsolidiert, ist die Verwendung eines Domänenereignisbusses.

Sie haben eine Singleton EventBus Klasse - einige verwenden eine statische Klasse, ich bevorzuge eine IEventBus Schnittstelle und injiziere den Ereignisbus in eine Klasse, die Ereignisse auslösen kann.

Dann registrieren Sie Klassen als Handler für bestimmte Arten von Ereignissen. So haben Sie möglicherweise eine HealthChangedEvent Klasse, eine IEventHandler<HealthChangedEvent> Schnittstelle, und dann registrieren Sie Klassen, die die Schnittstelle implementieren.

Das ist der Schlüssel - eine Klasse ist als Handler für einen Typ des Ereignisses registriert, so dass es keine einzelnen Klassen abonnieren muss, die Ereignisse veröffentlichen können. Wenn irgendwas einen HealthChangedEvent auslöst, dann empfängt es jeder registrierte Handler. Sie registrieren einige Zuhörer und abonnieren nicht viele Publisher.

Wenn Ihre Klasse eventBus.Raise(healthChangedEvent) aufruft, übergibt der Event-Bus das Ereignis an jeden registrierten Handler. Auf diese Weise können Sie beliebig viele Listener vom Sender entkoppeln.Sie wissen nur über das Ereignis. Sie wissen nichts über die Quelle, es sei denn, ein Verweis auf die Quelle wird mit dem Ereignis übergeben.

Es funktioniert besonders gut mit Abhängigkeitsinjektionscontainern, da der Container Instanzen von IEventHandler<TEvent> auflösen kann.

Hier ist ein blog post zum Erstellen Ihrer eigenen. Ich habe meine eigene Implementierung, die es mir ermöglicht, verschiedene DI-Container zu verwenden, ohne mit irgendwelchen gekoppelt zu werden. Ich werde das in einen Blogbeitrag einfügen, damit es einfacher ist, etwas zu teilen.


Aktualisierung: Here's my own implementation. Ich werde es irgendwann zusammen mit allen Komponententests in ein Repository legen.

+0

Vielen Dank für Ihre Antworten und Blogpost. Es hat einige Zeit gedauert, bis ich herausgefunden habe, wie ich das umsetzen kann. Der EventBus war wirklich das, was ich gesucht habe. Kannst du eines klarstellen: Ist ein EventBus wie ein Event-Aggregator? – milosa

+1

Ja - ich musste nachsehen, aber es ist eine andere Bezeichnung für die gleiche Sache. –

+1

Ich bin an allem interessiert, was ich tun kann, um es einfacher zu verwenden und zu verstehen. Vielen Dank –

2

Try this:

public class HealthChangedEventArgs 
{ 
    public HealthChangedEventArgs(int health) { Health = health; } 
    public int Health { get; private set; } // readonly 
} 

public class Unit 
{ 
    //The delegate - event handlers must have this signature 
    public delegate void HealthChangedEventHandler(object sender, HealthChangedEventArgs e); 

    // The event 
    public event HealthChangedEventHandler HealthChangedEvent; 

    //Method for raising the event - derived classes can also call it. 
    protected virtual void RaiseHealthChangedEvent() 
    { 
     // Raise the event by using the() operator. 
     if (HealthChangedEvent != null) 
      HealthChangedEvent(this, new HealthChangedEventArgs(health)); 
    } 

    private int health; 
    public int Health 
    { 
     get { return health; } 
     set 
     { 
      health = value; 
      RaiseHealthChangedEvent(); 
     } 
    } 
} 

Unit ist kein Verfahren auf UnitManager aufrufen. Es wird nur eine Veranstaltung ausgelöst. Es weiß nicht, was - wenn überhaupt - zuhört.

UnitManager ist verantwortlich für das Hinzufügen seiner Ereignishandler zu jeder Instanz von Unit und auf das Ereignis zu hören.

Verwandte Themen