Hub che esegue i metodi di interfaccia su tutte le istanze registrate

0

È possibile avere una classe generica (chiamiamola Hub), che implementa l'interfaccia T. E ora posso usare un metodo chiamato Add (non una parte o un'interfaccia T) su questa classe. E aggiungere metodi mi consente di aggiungere (memorizzare in elenco) istanze di T. Ora quando uso un metodo XYZ dall'interfaccia T, sul mio hub di classe, per esempio. Hub.XYZ (1), genererà un metodo XYZ con parametro 1 su tutte le istanze registrate nel mio elenco interno utilizzando il metodo Aggiungi.

Ad esempio:

var hub = Hub<IDisposable> ();
hub.Add (new SomeClassA ());
hub.Add (new SomeClassB ());
hub.Add (new SomeClassC ());

hub.Dispose (); // it will call Dispose on all 3 registered instances
    
posta zgnilec 27.09.2014 - 14:22
fonte

2 risposte

1

Non puoi farlo usando esattamente questa sintassi, perché i parametri generici non possono cambiare le interfacce implementate dalla tua classe (ad esempio non puoi scrivere qualcosa come class Hub<T> : T ).

Posso vedere due modi alternativi per farlo (ce ne sono probabilmente altri):

Un approccio semplice che utilizza lambdas

La tua classe Hub avrebbe un metodo come void Execute(Action<T> action) , che eseguirà il dato action per tutti gli elementi in esso.

Con questo, il tuo codice diventerebbe:

var hub = new Hub<IDisposable>();

hub.Add(new SomeClassA());
hub.Add(new SomeClassB());
hub.Add(new SomeClassC());

hub.Execute(x => x.Dispose());

L'implementazione di Hub potrebbe essere simile a:

public class Hub<T>
{
    private readonly List<T> items = new List<T>();

    public void Add(T item)
    {
        items.Add(item);
    }

    public void Execute(Action<T> action)
    {
        foreach (var item in items)
        {
            action(item);
        }
    }
}

Un approccio complicato che utilizza metaprogrammazione, che si traduce in una sintassi leggermente più semplice

Un altro modo sarebbe avere una proprietà del tipo di interfaccia sul Hub , che restituirebbe effettivamente un tipo generato in runtime che esegue l'azione per tutti gli elementi. Per generare questo tipo, puoi usare qualcosa come Castle DynamicProxy , o farlo manualmente usando Reflection.Emit.

Con questo, il tuo codice sarà simile a questo:

var hub = new Hub<IDisposable>();

hub.Add(new SomeClassA());
hub.Add(new SomeClassB());
hub.Add(new SomeClassC());

hub.Instance.Dispose();

Il codice per Hub che utilizza DynamicProxy è in realtà più semplice di quanto mi aspettassi:

public class Hub<T>
    where T : class
{
    private readonly List<T> items;
    private readonly T instance;

    public Hub()
    {
        items = new List<T>();
        var interceptor = new HubInterceptor(items);
        instance = new ProxyGenerator().CreateInterfaceProxyWithoutTarget<T>(interceptor);
    }

    public T Instance { get { return instance; } }

    public void Add(T item)
    {
        items.Add(item);
    }

    private class HubInterceptor : IInterceptor
    {
        private readonly IEnumerable<T> targets;

        public HubInterceptor(IEnumerable<T> targets)
        {
            this.targets = targets;
        }

        public void Intercept(IInvocation invocation)
        {
            foreach (var target in targets)
            {
                invocation.Method.Invoke(target, invocation.Arguments);
            }
        }
    }
}
    
risposta data 27.09.2014 - 16:32
fonte
0

I delegati di C # lo fanno già.

var hub = Action<int>();
hub += new SomeClassA().XYZ;
hub += new SomeClassB().XYZ;
hub += new SomeClassC().XYZ;
hub(1);

Ancora meglio, non hai realmente bisogno che le diverse classi abbiano un'interfaccia comune. Finché specifichi una funzione che richiede un int e restituisce void allora Action<int> funziona.

Il problema con questa implementazione è che le funzioni sono eseguite in ordine. Se si verifica un'eccezione in uno, la catena si ferma e non sai dove. L'alternativa è creare una classe che mantenga i delegati (o un'istanza della tua interfaccia) in un elenco, chiamandoli a turno.

    
risposta data 27.09.2014 - 17:52
fonte

Leggi altre domande sui tag