2015-12-11 11 views
9

Nel codice di esempio, perché la chiamata a ArrayMethod sicuro per un tipo genric quando non includo il vincolo classPassaggio di un array

public interface IFoo { } 
public interface IBar : IFoo { } 

public class Test 
{ 
    public void ClassConstraintTest<T>() where T : class, IFoo 
    { 
     T[] variable = new T[0]; 
     ArrayMethod(variable); 
    } 

    public void GenericTest<T>() where T : IFoo 
    { 
     T[] variable = new T[0]; 
     ArrayMethod(variable); // Compilation error: Can't convert T[] to IFoo[] 
    } 

    public void InheritanceTest() 
    { 
     IBar[] variable = new IBar[0]; 
     ArrayMethod(variable); 
    } 

    public void ArrayMethod(IFoo[] args) { } 
} 

risposta

9

è perché array covariance, cioè il fatto che MySubtype[] è un sottotipo di MyType[], funziona solo per i tipi di riferimento. Il vincolo class garantisce che T sia un tipo di riferimento.

(Si noti che, in retrospettiva, covarianza di matrice è considered to be a bad idea Cercate di evitare, se è possibile, ad esempio, facendo ArrayMethod generici o utilizzando IEnumerable<IFoo>, invece..)

+0

Un buon sistema di tipo dovrebbe avere un tipo covariante che può essere usato come una matrice i cui elementi possono essere letti, scambiati o copiati all'interno dell'array. I tipi di array in .NET e Java possono essere tranquillamente utilizzati in moda covariante come tali tipi e possono essere tranquillamente usati in modo non covariante come raccolte liberamente scrivibili. Forse sarebbe stato meglio avere più tipi di riferimenti agli array, con diverse abilità pubblicizzate, ma IMHO quelli che semplicemente affermano che gli array covarianti erano un "errore" non hanno davvero capito i costi delle alternative. – supercat

5

In breve: covarianza di matrice funziona solo quando entrambi gli array sono di tipo riferimento (class).

Per capire questo, è necessario comprendere il layout di memoria dei diversi tipi di array. In C# abbiamo array valore (int[], float[], DateTime[], eventuali definiti dall'utente struct[]) array dove ogni cosa è memorizzato sequenzialmente all'interno della matrice e di riferimento (object[], string[], eventuali definiti dall'utente class[], interface[] o delegate[]) riferimenti dove vengono archiviati sequenzialmente all'interno dell'array e gli oggetti vengono memorizzati all'esterno dell'array, ovunque si trovino nella memoria.

Quando si richiede che il lavoro metodo su qualsiasi T (senza la costrizione : class) si consente di uno di questi due tipi di matrici, ma il compilatore sa per un fatto che qualsiasi int[] (o qualsiasi altra matrice valore) non è andando in qualche modo magicamente diventare un valido IFoo[] (o qualsiasi altra matrice di riferimento) e vieta la conversione. Ciò accade anche se la propria struttura implementa IFoo per nessun altro motivo che IFoo[] è un array di riferimento e un T[] sarà un array di valori.

Tuttavia, quando si specifica che T è un tipo di riferimento (cioè la dichiarazione class), è ora possibile che il T[] è una valida IFoo[] perché entrambi sono matrici di riferimento. Quindi il compilatore consente il codice utilizzando le regole di covarianza dell'array (in cui è possibile utilizzare T[] dove è richiesto IFoo[] perché T è un sottotipo di IFoo).

Problemi correlati