Domanda

Recentemente ho trovato un comportamento molto sorprendente in c #. Avevo un metodo che utilizza IEnumerable < Object > come parametro e stavo passando IEnumerable < string > ma non è possibile. Mentre in c # tutto può essere trasferito su Object rispetto al motivo per cui questo non è possibile? È totalmente confuso per me. Per favore qualcuno mi chiarisca su questo problema.

È stato utile?

Soluzione

Il termine tecnico per questo è che i generici sono invarianti in C # 3.0 e precedenti. Da C # 4.0 in poi, il cast funziona.

Ciò che significa invariante è che non esiste alcuna relazione tra due tipi generici solo perché i loro tipi generici parametri sono correlati (ovvero sono sottotipi o supertipi tra loro).

Nel tuo esempio, non esiste alcuna relazione di battitura tra un IEnumerable < object > e un IEnumerable < string > , solo perché string è un sottotipo di oggetto. Sono solo considerati due tipi completamente non correlati, come una stringa e un int (sono ancora entrambi sottotipi di oggetto, ma tutto è)

Esistono alcune soluzioni alternative ed eccezioni per questo problema.

Innanzitutto, puoi eseguire il cast di ogni stringa singolarmente su object, se stai usando .NET 3.0 puoi farlo usando il metodo di estensione Cast < T > () . Altrimenti, puoi usare un foreach e inserire il risultato in una nuova variabile del tipo statico che desideri.

In secondo luogo, le matrici sono un'eccezione per il tipo di riferimento, vale a dire il passaggio di un tipo stringa [] a un metodo che accetta i tipi di oggetto [] dovrebbe funzionare.

Altri suggerimenti

Come altri hanno sottolineato, i tipi generici sono invarianti. IEnumerable < T > potrebbe essere una variante, ma C # al momento non supporta la specifica di varianti. Si prevede che C # 4.0 supporti le varianti, quindi potrebbe essere supportato in futuro.

Per aggirare questo problema ora puoi utilizzare un metodo di estensione LINQ Cast < object > () . Supponendo che tu abbia un metodo chiamato Foo che accetta un IEnumerable < object > > . Puoi chiamarlo così,

Foo(stringEnumerable.Cast<object>());

Il modo più semplice per passare IEnumerable < string > per funzionare richiedendo IEnumerable < object > è attraverso la conversione della funzione in questo modo:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

Quando uscirà C # 4, questo non sarà necessario, perché supporterà la covarianza e la contraddizione.

Dovresti usare

IEnumerable<T> 

se vuoi passare in tipi diversi, puoi interrogare T per scoprire di che tipo è.

Un buon modo di pensarci è chiederti " Cosa accadrebbe se potessi farlo? " ;. Prendi il seguente esempio:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

Abbiamo appena aggiunto un numero intero a un elenco di stringhe. Il compilatore lo fa per preservare la sicurezza dei tipi.

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