In C #, perché non è possibile archiviare un oggetto List <string> in una variabile List <object>


85

Sembra che un oggetto List non possa essere memorizzato in una variabile List in C # e non possa nemmeno essere espressamente cast in questo modo.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

risulta in Impossibile convertire implicitamente il tipo System.Collections.Generic.List<string>inSystem.Collections.Generic.List<object>

E poi...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

risulta in Impossibile convertire il testo System.Collections.Generic.List<string>inSystem.Collections.Generic.List<object>

Certo, puoi farlo estraendo tutto dall'elenco delle stringhe e rimettendolo in uno alla volta, ma è una soluzione piuttosto contorta.


5
Questo cambierà con C # 4.0, quindi potresti voler cercare covarianza e controvarianza. Consentirà tali cose in modo sicuro.
John Leidegren


7
John> C # 4 non lo consentirà. Pensa a ol.Add (new object ());
Guillaume

E 'rispose Jon Skeet qui: stackoverflow.com/a/2033921/1070906
JCH2k

Risposte:


39

Pensala in questo modo, se dovessi eseguire un cast del genere e quindi aggiungere un oggetto di tipo Foo all'elenco, l'elenco di stringhe non è più coerente. Se si ripetesse il primo riferimento, si otterrebbe un'eccezione di cast della classe perché una volta raggiunta l'istanza Foo, Foo non può essere convertito in stringa!

Come nota a margine, penso che sarebbe più significativo se puoi o meno fare il cast inverso:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Non ho usato C # per un po ', quindi non so se sia legale, ma quel tipo di cast è effettivamente (potenzialmente) utile. In questo caso, si passa da una classe (oggetto) più generale a una classe (stringa) più specifica che si estende da quella generale. In questo modo, se aggiungi alla lista delle stringhe, non stai violando la lista degli oggetti.

Qualcuno sa o può verificare se un tale cast è legale in C #?


4
Penso che il tuo esempio soffra dello stesso problema. Cosa succede se ol ha qualcosa che non è una stringa? Il problema è che alcuni metodi su List funzionerebbero bene, come l'aggiunta / l'inserimento. Ma l'iterazione potrebbe essere un vero problema.
Chris Ammerman,

3
Eric Lippert ha una grande serie di post sul blog su questo argomento: perché potrebbe funzionare per aggiungere vincoli di covarianza e controvarianza a metodi generici, ma potrebbe non funzionare mai come vorremmo a livello di classe. is.gd/3kQc
Chris Ammerman,

@ ChrisAmmerman: Se olavesse qualcosa che non è una stringa, suppongo che mi aspetterei che il cast fallisse in fase di esecuzione. Ma dove potresti davvero finire nei guai è se il cast avesse successo, e poi fosse aggiunto qualcosa olche non è una stringa. Poiché fa slriferimento allo stesso oggetto, ora il tuo List<string>conterrebbe una non stringa. Questo Addè il problema, che immagino giustifichi il motivo per cui questo codice non verrà compilato, ma verrà compilato se si List<object> olpassa a IEnumerable<object> ol, che non ha un file Add. (Ho controllato questo in C # 4.)
Tim Goodman

Con questa modifica, compila ma genera un InvalidCastExceptionperché il tipo di runtime di olè ancora List<object>.
Tim Goodman

36

Se stai usando .NET 3.5 dai un'occhiata al metodo Enumerable.Cast. È un metodo di estensione, quindi puoi chiamarlo direttamente nell'elenco.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Non è esattamente quello che hai chiesto, ma dovrebbe funzionare.

Modifica: come notato da Zooba, puoi quindi chiamare ol.ToList () per ottenere un elenco


14

Non è possibile eseguire il cast tra tipi generici con parametri di tipo diversi. I tipi generici specializzati non fanno parte dello stesso albero di ereditarietà e quindi sono tipi non correlati.

Per fare questo pre-NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Utilizzando Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

11

Il motivo è che una classe generica come List<>è, per la maggior parte degli scopi, trattata esternamente come una classe normale. ad esempio, quando dici che List<string>()il compilatore diceListString() (che contiene stringhe). [Gente tecnica: questa è una versione estremamente semplice in inglese di quello che sta succedendo]

Di conseguenza, ovviamente il compilatore non può essere abbastanza intelligente da convertire un ListString in un ListObject eseguendo il cast degli elementi della sua raccolta interna.

Ecco perché esistono metodi di estensione per IEnumerable come Convert () che consentono di fornire facilmente la conversione per gli elementi archiviati all'interno di una raccolta, il che potrebbe essere semplice come il casting da uno all'altro.


6

Ciò ha molto a che fare con la covarianza, ad esempio, i tipi generici sono considerati come parametri e se i parametri non si risolvono correttamente in un tipo più specifico, l'operazione fallisce. L'implicazione di tale è che non puoi davvero eseguire il cast su un tipo più generale come object. E come affermato da Rex, l'oggetto List non convertirà ogni oggetto per te.

Potresti invece provare il codice ff:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

o:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol copierà (teoricamente) tutti i contenuti di sl senza problemi.


5

Sì, puoi, da .NET 3.5:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();



1

In realtàèin modo che tu non provi a mettere alcun "oggetto" dispari nella tua variante di elenco "ol" (come List<object>sembrerebbe consentire) - perché il tuo codice si bloccherebbe allora (perché l' List<string>elencoè davveroe accetterà solo oggetti di tipo String ). Ecco perché non puoi eseguire il cast della tua variabile su una specifica più generale.

Su Java è il contrario, non hai generici, e invece tutto è Elenco di oggetti in fase di esecuzione, e puoi davvero inserire qualsiasi oggetto strano nel tuo Elenco presumibilmente strettamente tipizzato. Cerca "Reified generics" per visualizzare una discussione più ampia del problema di java ...


1

Tale covarianza sui generici non è supportata, ma puoi effettivamente farlo con gli array:

object[] a = new string[] {"spam", "eggs"};

C # esegue controlli di runtime per impedire di inserire, ad esempio, un file intin a.


0

Ecco un'altra soluzione pre-.NET 3.5 per qualsiasi IList i cui contenuti possono essere espressi in modo implicito.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(Basato sull'esempio di Zooba)


0

Ho un:

private List<Leerling> Leerlingen = new List<Leerling>();

E stavo per riempirlo con i dati raccolti in un List<object> Ciò che alla fine ha funzionato per me era questo:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castal tipo che si desidera ottenere un IEnumerableda quel tipo, quindi typecast la IEnemuerablealla List<>desiderata.


0

Mm, grazie ai commenti precedenti ho trovato due modi per scoprirlo. Il primo è ottenere l'elenco di stringhe di elementi e quindi trasmetterlo all'elenco di oggetti IEnumerable:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

E il secondo è evitare il tipo di oggetto IEnumerable, semplicemente eseguire il cast della stringa sul tipo di oggetto e quindi utilizzare la funzione "toList ()" nella stessa frase:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Mi piace di più il secondo modo. Spero che questo possa essere d'aiuto.


0
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.