Domanda

Varianza generico in C# 4.0 è stato implementato in modo che è possibile scrivere le seguenti senza eccezione (che è quello che accadrebbe in C# 3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

[Esempio non funzionali:Vedere Jon Skeet risposta]

Recentemente ho partecipato a una conferenza in cui Jon Skeet ha dato un'eccellente panoramica della Varianza Generico, ma non sono sicuro che io sono completamente ottenerlo - ho capito il significato del in e out parole chiave quando si tratta di contra e co-varianza, ma sono curioso di cosa succede dietro le quinte.

Che cosa fa il CLR vedere quando viene eseguito questo codice? E ' implicito di conversione del List<int> per List<object> o è semplicemente costruito in che ora è possibile la conversione tra i tipi derivati per tipi padre?

Di interesse, perché non era questo introdotto nelle versioni precedenti e qual è il vantaggio principale - ie mondo reale utilizzo?

Maggiori informazioni su questo post per la Varianza Generica (ma la questione è estremamente obsoleto, cercando reale, up-to-date informazioni)

È stato utile?

Soluzione

No, il tuo esempio non avrebbe funzionato per tre motivi:

  • Classi (come List<T>) sono invarianti;solo i delegati e le interfacce di variante
  • Per la varianza di lavoro, l'interfaccia deve utilizzare solo il tipo di parametro in una direzione (per la controvarianza, per covarianza)
  • Tipi di valore non sono supportati come argomenti di tipo per la varianza, quindi non c'è conversione da IEnumerable<int> per IEnumerable<object> per esempio

(Il codice non riesce a compilare in C# 3.0 e 4.0 - non c'è nessuna eccezione).

Quindi questo sarebbe lavoro:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

Il CLR utilizza solo il riferimento, invariato - non vengono creati nuovi oggetti.Quindi, se si chiama objects.GetType() dovresti ancora avere List<string>.

Credo che non è stato introdotto in precedenza, perché il linguaggio dei progettisti doveva ancora definire i dettagli di come esporre - è stato in CLR dal v2.

I benefici sono gli stessi delle altre volte in cui si desidera essere in grado di utilizzare un tipo di un altro.Per usare lo stesso esempio che ho usato sabato scorso, se hai qualcosa implementa IComparer<Shape> per confrontare le forme in base alla zona, è pazzesco che non è possibile utilizzare che per ordinare un List<Circle> se non è possibile confrontare due forme, si può certamente paragonare due cerchi.Come di C# 4, non ci sarebbe un controvariante di conversione da IComparer<Shape> per IComparer<Circle> così si potrebbe chiamare circles.Sort(areaComparer).

Altri suggerimenti

Un paio di ulteriori pensieri.

Che cosa fa il CLR vedere quando viene eseguito questo codice

Come Jon e gli altri hanno correttamente osservato, non stiamo facendo varianza sulle classi, solo le interfacce e i delegati.Quindi, nel tuo esempio, il CLR non vede niente;che il codice non viene compilato.Se lo si forza a compilare inserendo abbastanza getta, si blocca in fase di runtime con un brutto cast di eccezione.

Ora, è ancora una domanda ragionevole chiedersi come varianza lavora dietro le quinte, quando funziona.La risposta è:la ragione per cui siamo limitare questo tipo di riferimento argomenti parametrizzazione interfaccia e i tipi di delegato è così che nulla succede dietro le quinte.Quando si dice

object x = "hello";

cosa succede dietro le quinte, è il riferimento alla stringa è bloccato nella variabile di tipo object senza modifiche.I bit che costituiscono un riferimento a una stringa sono legali bit per essere un riferimento a un oggetto, in modo che non deve accadere qui.Il CLR semplicemente smette di pensare a quei bit come riferimento a una stringa e inizia a pensare a loro come un riferimento a un oggetto.

Quando dici:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = e1;

Stessa cosa.Non accade nulla.I bit che fanno riferimento a una stringa enumeratore sono gli stessi come i bit che compongono un riferimento a un oggetto enumeratore.C'è un po ' più di magia che entra in gioco quando si fare un cast, dicono:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = (IEnumerator<object>)(object)e1;

Ora CLR deve generare un controllo che e1 effettivamente implementare tale interfaccia, e che il controllo deve essere intelligente su come riconoscere la varianza.

Ma il motivo, si può ottenere via con variante interfacce da solo no-op conversioni è perché regolare la compatibilità di assegnazione è in questo modo.Che cosa hai intenzione di utilizzare e2 per?

object z = e2.Current;

Che restituisce i bit che sono un riferimento a una stringa.Abbiamo già stabilito che sono compatibili con quelle oggetto senza modificare.

Perché non era questo introdotto in precedenza?Abbiamo avuto altre caratteristiche da fare e con un budget limitato.

Qual è il principio di beneficio?Che le conversioni da una sequenza di stringa sequenza di oggetti "solo lavoro".

Di interesse, perché non era questo introdotte nelle versioni precedenti

La prima versione (1.x).NET non sono generici, in modo generico, varianza, lontano.

Va notato che in tutte le versioni di .NET, non c'è matrice di covarianza.Purtroppo, è pericoloso covarianza:

Apple[] apples = new [] { apple1, apple2 };
Fruit[] fruit = apples;
fruit[1] = new Orange(); // Oh snap! Runtime exception! Can't store an orange in an array of apples!

Il co - e contro-varianza in C# 4 è sicuro, e per evitare questo problema.

qual è il vantaggio principale - ie reale utilizzo del mondo?

Molte volte nel codice, che si sta chiamando un API si aspetta un amplificato tipo di Base (ad es. IEnumerable<Base>), ma tutto ciò che ho è amplificata tipo di Derivato (es. IEnumerable<Derived>).

In C# 2 e C# 3, hai bisogno di convertire manualmente per IEnumerable<Base>, anche se si deve "solo lavoro".Co - e contro-varianza rende "solo lavoro".

p.s.Totalmente schifo che il tiro al Piattello, la risposta è mangiare tutti i miei punti rep.Accidenti, il tiro al Piattello!:-) Sembra che sia risposto a questa prima, però.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top