2013-03-24 11 views
5

Non sono sicuro di quello che sta succedendo qui, ma sto ottenendo un errore di compilazione utilizzando il seguente codice:C# Generics interfaccia covarianza

namespace SO 
{ 
    interface IUser<PostType> 
    { 
     PostType Post { get; set; } 
    } 

    interface IPost<UserType> 
    { 
     UserType User { get; set; } 
    } 

    class User : IUser<Post> 
    { 
     //Implementation 
    } 

    class Post : IPost<User> 
    { 
     //Implementation 
    } 

    class SomeOtherClass 
    { 
     // Compiler Error: Cannot implicitly convert type 'SO.User' to 
     // 'SO.IUser<SO.IPost<SO.User>>'. An explicit conversion exists 
     // (are you missing a cast?) 
     IUser<IPost<User>> user = new User(); 

     //Works Fine 
     IUser<Post> user = new User(); 
    } 
} 

Perché ricevo un errore se Post è un sottotipo di IPost<User>? So che in questo caso potrei semplicemente usare User invece di IUser<IPost<User>>, ma voglio sapere perché questo non funziona.

+0

Si può iniziare [qui ] (http://blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx) –

risposta

12

cercherò di spiegarlo utilizzando semplice esempio. Supponiamo di avere una classe più attuazione IPost<User>:

class PicturePost : IPost<User> 
{ 
    // Implementation 
} 

Allora questo codice non viene compilato:

IUser<Post> user = new User(); 
    user.Post = new PicturePost(); 

Perché user.Post è di classe concreta Post che non è compatibile con PicturePost (sono fratelli).

Poi immaginiamo che la linea dalla tua domanda è stato compilato con successo:

// C# compiler is so kind today and it compiled this. 
    IUser<IPost<User>> user = new User(); 

Dal user.Post ora sarà di tipo IPost<User> potenzialmente si dovrà codificare tali linee:

IUser<IPost<User>> user = new User(); 
    user.Post = new PicturePost(); 

e saranno compilare perfettamente, ma la seconda riga avrà esito negativo con l'errore in fase di esecuzione! Questo perché il tipo effettivo di user.Post è Post non IPost o PicturePost.

Quindi, per ottenere la sicurezza del tipo, il compilatore C# proibisce la compilazione se esiste la possibilità che tale codice venga scritto. Al fine di garantire che non sarà possibile scrivere questo codice, Post proprietà dovrebbe essere di sola lettura:

interface IUser<PostType> 
{ 
    PostType Post { get; } // No setter, this is readonly. 
} 

Ora non sarà in grado di scrivere codice maligno, e tutti gli utilizzi di Post sarà type-safe nei confronti della sua interfaccia, dal momento che si può solo ottenere esso e che assegnare perfettamente alla variabile della sua interfaccia.

Ma questo non è sufficiente, per dire compilatore che l'interfaccia sul lato chiaro, è necessario specificare in modo esplicito che il parametro di tipo è solo fuori (si può usare, ma non si può passare in). Così, avendo seguito implementazione dell'interfaccia (si noti out parola chiave), il codice verrà compilato:

interface IUser<out PostType> 
{ 
    PostType Post { get; } // No setter, this is readonly. 
} 

    // Both lines compile! 
    IUser<IPost<User>> user = new User(); 
    IUser<Post> user1 = new User(); 

Speranza ho mantenuto semplice e non ha mancato il punto allo stesso tempo :)