2015-08-06 15 views
7

Ho la seguente interfaccia:Impossibile convertire tipo concreto alla versione generica della sua interfaccia in C#

public interface INotificationHandler<T> 
{ 
    Task<T> Handle(string msg); 
} 

E diverse classi che felicemente implementare in questo modo:

public class FooHandler : INotificationHandler<Foo> 
{ 
    public Task<Foo> Handle(string msg) { return Task.FromResult<Foo>(new Foo()); } 
} 

public class BarHandler : INotificationHandler<Bar> 
{ 
    public Task<Bar> Handle(string msg) { return Task.FromResult<Bar>(new Bar()); } 
} 

mi piacerebbe come mantenere una raccolta di istanze di INotificationHandler in una raccolta e quando ricevo un messaggio "foo", usa FooHandler, "bar" ottiene BarHandler, ecc ...

var notificationHandlers = new Dictionary<string, INotificationHandler<object>>(); 
notificationHandlers["foo"] = new FooHandler(); 
notificationHandlers["bar"] = new BarHandler(); 
... 
public void MessageReceived(string type, string msg) 
{ 
    INotificationHandler<object> handler = notificationHandlers[type]; 
    handler.Notify(msg).ContinueWith((result) => /* do stuff with a plain object */) 
} 

Tuttavia questo non riesce a compilare perché il mio generico non ha un tipo di base comune, che è di progettazione. Qualsiasi oggetto dovrebbe essere in grado di essere restituito da un INotificationHandler in MessageReceived.

Impossibile convertire implicitamente il tipo FooHandler in INotificationHandler<object>. Esiste Una conversione esplicita (che le manca un cast?)

Come posso lavorare con INotificationHandler<T> in modo che non ho bisogno di cure circa i tipi generici di sue implementazioni concrete?

+2

Hai provato a dichiarare l'interfaccia pubblica INotificationHandler ? –

+0

Secondo me questo sconfigge completamente lo scopo dei generici. – juharr

+0

@JerryFederspiel L'ho provato, tuttavia T deve essere invariabilmente valido? Scostamento non valido: il parametro tipo "T" deve essere invarialmente valido su "INotificationHandler . Handle (stringa)". 'T' è covariante. – plemarquand

risposta

2

Se è necessario un tipo di sicurezza, è possibile utilizzare la seguente gerarchia.

public interface INotificationHandler 
{ 
    Task<object> Handle(string msg); 
} 

public abstract BaseHandler<T> : INotificationHandler 
{ 
    Task<object> INotificationHandler.Handle(string msg) 
    { 
     return Handle(msg); 
    } 

    public abstract Task<T> Handle(string msg); 
} 

public class FooHandler : BaseHandler<Foo> 
{ 
    public override Task<Foo> Handle(string msg) { return Task.FromResult<Foo>(new Foo()); } 
} 

public class BarHandler : BaseHandler<Bar> 
{ 
    public override Task<Bar> Handle(string msg) { return Task.FromResult<Bar>(new Bar()); } 
} 

var notificationHandlers = new Dictionary<string, INotificationHandler>(); 
notificationHandlers["foo"] = new FooHandler(); 
notificationHandlers["bar"] = new BarHandler(); 
... 
public void MessageReceived(string type, string msg) 
{ 
    INotificationHandler handler = notificationHandlers[type]; 
    handler.Notify(msg).ContinueWith((result) => /* do stuff with a plain object */) 
} 
+0

Questo non funzionerà se FooHandler e BarHandler non possono eredarsi dallo stesso BaseHandler. Come sappiamo, l'oggetto può ereditare solo da una classe. –

+0

Entrambi sono ereditati dallo stesso BaseHandler –

+0

Sì, è vero nel tuo caso. Immagina di avere già una classe FooHandler che eredita da A e BarHandler che eredita da B (diciamo che A è Windows Control e B è Web Control). In questo caso la tua architettura non può funzionare perché FooHandler e BarHandler non potranno ereditare da BaseHandler. Questo è quello che volevo sottolineare. –

Problemi correlati