2015-12-29 13 views
6

Amo i generici in C#, ma a volte lavorare con loro può diventare un po 'complicato. Il problema qui sotto mi imbatte di tanto in tanto. C'è un modo per semplificare questo scenario? Non riesco a vedere come, ma spero che qualcuno possa :)È possibile semplificare i generici annidati in C#?

Dati tre classi di base:

public abstract class Inner 
{ 
} 

public abstract class Outer<T> 
    where T : Inner 
{ 
} 

public abstract class Handler<TOuter, TInner> 
    where TOuter : Outer<TInner> 
    where TInner : Inner 
{ 
    public abstract void SetValue(TInner value); 
} 

E alcune semplici implementazioni:

public class In : Inner 
{ 
} 

public class Out : Outer<In> 
{ 
} 

public class HandleOut : Handler<Out, In> 
{ 
    public override void SetValue(In value) { } 
} 

Ora la mia domanda è: per HandleOut, il tipo di TInner è dato dal tipo "Out", quindi esiste un modo per semplificare la definizione di HandleOut in qualcosa come public class HandleOut : Handler<Out> e ancora essere in grado di utilizzare il tipo interno come parametro per SetValue?

Questo è un esempio molto semplice, ma a volte ottengo una lunga lista di tipi generici nelle definizioni, quando di solito tutti possono essere dedotti dal primo tipo logicamente. Ci sono trucchi che mi mancano?

+2

Ho corretto il codice in modo che venga compilato. Puoi controllare per vedere se è corretto dal POV della tua domanda? – Enigmativity

+4

Anche se non è allineato al 100% con quello che penso tu stia chiedendo, potresti voler leggere un post sul blog di Eric Lippert intitolato [Perché i vincoli generici non sono ereditati] (http://ericlippert.com/2013/07/15/ why-are-generic-constraints-not-inherited /) (e, infatti, il fatto che non siano ereditati è il motivo per cui Enigmativity ha dovuto correggere il codice per aggiungere * più * vincoli) –

+0

Grazie, @Enigmativity. Ho appena scritto il codice al volo - le tue modifiche sono esattamente secondo i miei pensieri :) –

risposta

1

No.

Mentre tale deduzione dovrebbe probabilmente essere possibile, non fa parte del languge. Potresti essere interessato a suggerire questo a Roslyn (aprire un nuovo numero). Ovviamente questo tipo di inferenza dei vincoli generici può incorrere in problemi in casi complessi, ma almeno per quelli semplici è fattibile ... tuttavia, è quello in cui il team C# dovrebbe dedicare tempo e sforzi?

Il collegamento Why are generic constraints not inherited che Damien_The_Unbelievershared on the comments è azzeccato.


In ogni caso, il codice tua presentata se è vero che Out già darvi il tipo In, il parametro generico TOuter non è necessaria.

Il seguente codice funziona altrettanto bene:

public abstract class Inner 
{ 
} 

public abstract class Outer<T> 
    where T : Inner 
{ 
} 

public abstract class Handler<TInner> // NOTE: TOuter removed 
    where TInner : Inner 
{ 
    public abstract void SetValue(TInner value); 
} 

public class In : Inner 
{ 
} 

public class Out : Outer<In> 
{ 
} 

public class HandleOut : Handler<In> // NOTE: Out removed 
{ 
    public override void SetValue(In value) { } 
} 

Quindi, potrebbe considerare l'utilizzo di Outer<TInner> invece di touter se ne avete bisogno. Ovviamente, se si mantiene un elenco di TOuter sarà meno restrittivo in quanto consentirà qualsiasi tipo derivato di Outer<TInner> invece di qualsiasi tipo derivato di TOuter.

Poiché non è stato inserito "nuovo" nel vincolo generico, non si stanno creando oggetti di questo tipo, ma se il caso arriva, è possibile accettare un Func<Outer<TInner>> nel costruttore.

+0

Grazie per la risposta. Hai ragione a ritenere che potrei perdere il TOuter nel mio esempio, ma questo è dovuto al fatto di non dare un esempio di vita reale - avrei bisogno del TOuter nei miei casi di vita reale.Per quanto riguarda se questo è il genere di cose su cui il C# deve passare del tempo, non ne sono sicuro. Potrebbe essere solo il mio stile di codifica che spesso mi mette in questo tipo di problema, ma troverei molto utile e un'ottima astrazione. Forse qualcosa del genere: public class class Handler dove ...? –

+0

@ChrisRidge che la sintassi può funzionare, ma difficilmente con il modo in cui il sistema viene attualmente eseguito poiché qualsiasi parametro implicito o non implicito verrà perso. Ecco perché lo vedo come inferenza sul tipo derivato. Quanti parametri generici hai? [Non riesco a pensare a un caso per più di 4] forse è un vantaggio di quello che stai sviluppando, o forse ti manca qualche astrazione ... Eppure se si attenua un caso particolare forse c'è un interesse da parte del team di Roslyn. Considera facoltativo: non è realmente necessario, ma facilita la manutenzione dell'interop e del codice. – Theraot

+0

Nel mio attuale progetto non ne ho più di quattro, ma queste sono in interfacce che vengono implementate molte volte. Dovendo specificare tutti e quattro i tipi per ogni implementazione è noioso e rende il codice più difficile da capire. Questa non è assolutamente una caratteristica indispensabile, ma per noi sarebbe una caratteristica fantastica! –