2010-06-21 2 views
16

Supponiamo che io ho la seguente gerarchia di classe:C# generics - Posso fare T essere da una delle due scelte?

Class A {...} 

Class B : A {...} 

Class C : A {...} 

Quello che ho attualmente è

Class D<T> where T : A {...} 

ma mi piacerebbe qualcosa di forma

Class D<T> where T in {B,C} 

Ciò è dovuto a qualche comportamento strano non sono responsabile per dove B e C hanno metodi comuni che non sono in A, ma sarebbe bello poterli chiamare in D su T.

Nota: non ho accesso a A, B o C per modificarli

risposta

23

È necessario definire un'interfaccia per i metodi comuni che sono in B e C (consente di chiamare Ibc), fare B e C implementare questa interfaccia, e quindi si può scrivere:

Class D<T> where T : A, Ibc {...} 
+6

Questo è un buon rootbeer ... – Nate

+7

Ma, non controlla le sorgenti A, B e C. Renderà questa soluzione un po 'difficile da implementare. – GvS

+0

Ah Non ho visto la modifica. Se tale interfaccia non esiste, non esiste un modo diretto per implementare la classe D per quanto ne so. – Grzenio

3

Do B e C implementano la stessa interfaccia? Potrebbe essere una strada migliore.

2

Alcune opzioni :

  1. Fai un'interfaccia IderivedFromA che contengono i metodi comuni da B e C.
    Sembra che questo è impossibile dalla tua domanda
  2. In D gettato T-dynamic e chiamare i metodi in modo dinamico
    La soluzione più facile, se è possibile utilizzare .Net 4
  3. In D prova se il avete a che fare con una B o C, cast, e chiamare
    saranno controllati dal compilatore, e è possibile da Net 2
  4. The Dan Tao answer: Creare un'implementazione specifica di D<T> per B e C, questi possono chiamare direttamente i metodi da B e C. (Non ho pensato a questo me stesso).
    Funzionerà solo se la "sorgente utente" sa che si sta occupando di B o C e non usa l'abstract A per utilizzare D<A>. Invece dovrebbe usare DB o DC. Ma penso che sia così, altrimenti non avevi bisogno di farmaci generici.
0

Il vincolo in C# non consente di specificare più classi come scelta. Anche se specificherete più dove contiene, allora entrambi devono essere soddisfatti. Non esiste una logica OR per il vincolo. Ecco le specifiche: http://msdn.microsoft.com/en-us/library/bb384067.aspx

Le risposte da Grzenio sembrano giuste per te. Estrarre un comportamento comune nell'interfaccia comune per B e C. Quindi è possibile utilizzare tale interfaccia come vincolo.

8

Questo non è direttamente possibile.

Come altri suggeriscono, è possibile definire un'interfaccia e implementarla sia in B e C.

Se questa non è un'opzione (ad esempio, se queste classi non sono sotto il tuo controllo), ciò che potrei suggerire è questa: in primo luogo, iniziare con una classe astratta che include tutte le funzionalità che è possibile ottenere con qualsiasi T derivante da A. Quindi affermare di disporre di alcuni metodi esistenti sia per B e C che non fanno parte di A. In D è possibile rendere questi metodi astratti per essere attuate da sottoclassi:

public abstract class D<T> where T : A 
{ 
    protected T _member; 

    public void DoSomethingAllTsCanDo() 
    { 
     _member.DoSomething(); 
    } 

    public abstract void DoSomethingOnlyBAndCCanDo(); 
} 

Quindi è possibile ereditare dalla classe di base per ogni tipo di B e C e sovrascrivere il metodo astratto (s) per fornire la funzionalità appropriata:

public class DB : D<B> 
{ 
    public override void DoSomethingOnlyBAndCCanDo() 
    { 
     _member.DoSomethingOnlyBCanDo(); 
    } 
} 

public class DC : D<C> 
{ 
    public override void DoSomethingOnlyBAndCCanDo() 
    { 
     _member.DoSomethingOnlyCCanDo(); 
    } 
} 
0

dal momento che non si ha accesso alla fonte, l'unica vera risposta (a meno che non si è disposti a perdere la sicurezza utilizzando dynamic) è esplicitamente verificare la presenza di B/C e cast.

5

In primo luogo, se B e C hanno metodi comuni, è un difetto di progettazione che non condividono un'interfaccia. Detto questo, puoi aggiustarlo anche senza avere accesso a B e C.
È possibile creare un'interfaccia comune. Supponete di avere:

public class A 
{ 
} 
public class B : A 
{ 
    public void Start() { } 
} 
public class C : A 
{ 
    public void Start() { } 
} 

È possibile creare un'interfaccia comune:

public interface IStartable 
{ 
    void Start(); 
} 

e usarlo su classi derivate da B e C:

public class BetterB : B, IStartable 
{ 
} 
public class BetterC : C, IStartable 
{ 
} 

Potrebbe non essere in grado di raggiungere che se ottieni istanze B e C così com'è, ma può essere considerato se le crei. Infatti, con le classi specializzate di B e C, è possibile utilizzare l'interfaccia al posto di D<T>.