C#泛型接口协方差

时间:2013-03-24 07:02:23

标签: c# generics covariance

我不确定这里发生了什么,但我使用以下代码收到编译错误:

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();
    }
}

如果PostIPost<User>的子类型,我为什么会收到错误?我知道在这种情况下我可以使用User代替IUser<IPost<User>>,但我想知道为什么这不起作用。

2 个答案:

答案 0 :(得分:12)

我将尝试使用简单的示例来解释它。假设您还有一个实现IPost<User>的类:

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

然后这段代码将无法编译:

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

因为user.Post具有与Post不兼容的具体类PicturePost(他们是兄弟姐妹)。

然后想象你问题中的那一行是否成功编译:

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

由于user.Post现在属于IPost<User>类型,您可能会对此类代码进行编码:

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

他们会完美编译,但第二行会因运行时错误而失败!这是因为user.Post的实际类型为Post而不是IPostPicturePost

因此,为了实现类型安全,C#编译器禁止编译是否有可能编写此类代码。为了确保您不会编写此类代码,Post属性应该是只读的:

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

现在你将无法编写恶意代码,Post的所有用法在其界面方面都是类型安全的,因为你可以获取它,而不是完美地分配给它的接口变量。

但这还不够,告诉编译器你的界面在光线方面,你需要明确指出你的类型参数只是 out (你可以使用它,但你不能通过它进入)。因此,在接口(通知out关键字)的实现之下,您的代码将编译:

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();

希望我保持简单并且不会同时忽略这一点:)

答案 1 :(得分:0)

嗯,你必须使你的界面协变:

interface IUser<out PostType>
{
    PostType Post { get; }
}

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

请参阅 http://msdn.microsoft.com/en-gb/library/ee207183.aspx http://msdn.microsoft.com/en-gb/library/dd799517.aspx

相关问题