2010-02-13 17 views
8

Diciamo che ho un'interfaccia simile:Tipo vincoli sulle implementazioni dei membri generiche di interfacce non generici in C#

interface IAwesome 
{ 
    T DoSomething<T>(); 
} 

C'è un modo per implementare il metodo DoSomething con tipo di vincolo? Ovviamente, questo non funzionerà:

class IncrediblyAwesome<T> : IAwesome where T : PonyFactoryFactoryFacade 
{ 
    public T DoSomething() 
    { 
     throw new NotImplementedException(); 
    } 
} 

Questo ovviamente non funzionerà perché questo DoSomething() non sarà soddisfare pienamente il contratto di IAwesome - funziona solo per un sottoinsieme di tutti i possibili valori del parametro type T. C'è un modo per ottenere questo lavoro a corto di alcuni "casting black magic" (che è quello che farò come ultima risorsa se la risposta è no)?

Onestamente, non penso sia possibile ma mi chiedo cosa ne pensate voi ragazzi.

EDIT: L'interfaccia in questione è System.Linq.IQueryProvider quindi non posso modificare l'interfaccia stessa.

risposta

7

No, questo non può funzionare in base alla progettazione, poiché significherebbe che il contratto per IAwesome non sarà (pienamente) soddisfatto.

Finché IncrediblyAwesome<T> attrezzi IAwesome, uno è permesso di fare questo:

IAwesome x = new IncrediblyAwesome<Something>() 

Ovviamente, con il vincolo aggiuntivo, questo non potrebbe funzionare, in quanto l'utente di IAwesome non può conoscere delle restrizioni imposte su di esso .

Nel tuo caso, l'unica soluzione che posso pensare è questo (facendo runtime controllo):

interface IAwesome { // assuming the same interface as in your sample 
    T DoSomething<T>(); 
} 

class IncrediblyAwesome<TPony> : IAwesome where TPony : PonyFactoryFactoryFacade { 
    IAwesome.DoSomething<TAnything>() { 
     return (TAnything)((object)DoSomething()); // or another conversion, maybe using the Convert class 
    } 

    public TPony DoSomething() { 
     throw new NotImplementedException(); 
    } 
} 
+0

Sì, è quello che temevo. –

+1

Hai usato "T" per indicare tre cose diverse ma strettamente correlate in questo codice. Questo è molto confuso; puoi farne nominare qualcos'altro? –

+0

Sì, mi dispiace, il copia-incolla è malvagio ... Risolto - grazie per il suggerimento. ;) – Lucero

2

Non farebbe una cosa del genere?

interface IAwesome<U> 
{ 
    T DoSomething<T>() where T : U 
} 

class IncrediblyAwesome<T> : IAwesome<PonyFactoryFactoryFacade> 
{ 
    public T DoSomething() 
    { 
     throw new NotImplementedException(); 
    } 
} 

Non sono sicuro se questo compila.

+0

Sfortunatamente, l'interfaccia in questione non è definita da me - fa parte del framework .NET (System.Linq.IQueryProvider) quindi non posso davvero mettere i vincoli di tipo sul inteface. Grazie per il suggerimento però. –

+0

Puoi scappare con IAwesome ereditando IQueryProvider? O potrebbe rompere il tuo codice? – Zyphrax

+0

Ciò non aiuta neanche perché non è possibile limitare un metodo esistente in un'interfaccia; tutte le garanzie fornite dall'interfaccia di base devono rimanere valide per tutti i discendenti e gli implementatori (dal punto di vista del contratto, naturalmente, è possibile effettuare controlli di runtime se necessario). – Lucero

1

Come hai già accennato, tale soluzione non può funzionare.

Inoltre, il vostro esempio viola il contratto in un altro modo: Un esempio di IncrediblyAwesome è sempre legato a una discendente specifico di PonyFactoryFactoryFacade (a proposito, sarei davvero interessa lo scopo di questa classe :-)). Pertanto, l'implementazione concreta di DoSomethingnon è un metodo generico come specificato nell'interfaccia, ma restituisce sempre un singolo tipo. È couln't scrittura:

IAwesome a = new IncrediblyAwesome<SomePonyFacadeFactory1>(); 
SomePonyFacadeFactory2 facade2 = a.DoSomething<SomePonyFacadeFactory2>(); 

anche se entrambe le facciate-fabbriche decend da PonyFactoryFactoryFacade ...

Un altro commento: vorrei stare lontano dal nero di colata magia, dal momento che questi problemi sono inerenti al vostro disegno e non solo un difetto di C# ...