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);
}
}
}
}