2009-04-21 4 views
21

È possibile definire una funzione che accetta un parametro che deve implementare due interfacce?È possibile fare un parametro per implementare due interfacce?

(Le due interfacce sono quelle che ho appena ricordato la parte superiore della mia testa, non quelli che vogliono usare)

private void DoSomthing(IComparable, ICollection input) 
{ 

} 
+0

È possibile utilizzare [lo stesso trucco utilizzato per dichiarare una variabile membro] (http://stackoverflow.com/a/4940249/429091), sebbene questo approccio richieda l'implementazione di classi per l'attivazione del modello. – binki

risposta

49

È possibile:

1) Definire un'interfaccia che eredita due interfacce richieste:

public interface ICombinedInterface : IComparable, ICollection {... } 

private void DoSomething(ICombinedInterface input) {... } 

2) Utilizzare farmaci generici:

private void DoSomething<T>(T input) 
    where T : IComparable, ICollection 
{...} 
+3

in VB.Net: DoSomthing Sub privato (di T come {IComparable, ICollection }) (ByVal input As T) ...End Sub – Pondidum

+0

Esiste un vantaggio nell'usare una di queste soluzioni rispetto all'utilizzo di due parametri separati? Intuitivamente sembra che sarebbe utile, ma sto facendo fatica a pensare ad un esempio concreto. – CurtainDog

+0

Non mi viene in mente uno in questo momento, ma volevo fare questo pochi giorni fa. e avere un parametro sembra migliore di chiamare DoSomthing (testData, testData) e quindi passare la stessa istanza in due volte. – Pondidum

5

È possibile ereditare un'altra interfaccia da quei due interfacce e rendere il vostro parametro implementare che interfaccia.

+1

funziona solo se è possibile modificare TUTTE le classi che implementano entrambe le interfacce per implementare la nuova interfaccia. –

+0

Beh, non TUTTI necessariamente. Solo quelli che devi usare come parametro per quel metodo. –

2

Beh, sì e no.

È possibile, come suggerito da Steve, creare un'altra terza interfaccia che discende dalle due che si desidera e utilizzarla per il tipo di parametro.

Tuttavia, ciò imporrà anche che la classe in uso esegua anche quella terza interfaccia.

In altre parole, questo non funzionerà:

public interface I1 { } 
public interface I2 { } 
public class C : I1, I2 { } 

public interface I3 : I1, I2 { } 
public class YourClass 
{ 
    public void Test(I3 i) { } 
} 

... 
YourClass yc = new YourClass(); 
C c = new C(); 
yc.Test(c); // won't work, C does not implement I3 

Tuttavia, si può ingannare il compilatore in ciò che si vuole per mezzo di farmaci generici.

public class YourClass 
{ 
    public void Test<T>(T i) where T: I1, I2 { } 
} 

Ora funzionerà. Non sono ancora sicuro al 100% che non ti daranno altri problemi, dovrei pensarci.

1

L'approccio generica funzione di cui sopra è buona, con un grande avvertimento: per typecast un oggetto in modo che possa essere passato a una routine con più vincoli generici, bisogna conoscere un tipo che soddisfi detti vincoli e è un tipo principale dell'oggetto da trasmettere. Una routine che accetta un tale oggetto conoscerà un tale tipo (è stato passato come parametro di tipo generico) ma non esiste un modo carino per una classe di conservare tali informazioni e usarle in seguito. Se una varietà di tipi non collegati implementano sia IFoo che IBar, sarebbe difficile progettare una routine che potrebbe accettare un numero di istanze di oggetti non correlate, memorizzarle in un elenco o qualcosa del genere e quindi passare tutti gli elementi nell'elenco a una routine con un parametro IFoo + IBar generico.

Se un tale scenario potrebbe essere necessario, l'approccio migliore sarebbe quello di avere una controparte non generica per ciascuna routine generica che accetterebbe ad es. un parametro di tipo IFoo e gettalo su un IBar se necessario. Uno potrebbe quindi memorizzare tutti gli articoli in un elenco <IFoo> e passarli alla routine. Si perderebbe la sicurezza del tipo del metodo generico, ma a volte la perfetta sicurezza del tipo non è realizzabile.