C#泛型接口协变性

5

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

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>>,但我想知道为什么这样做不起作用。


你可以从这里开始(http://blogs.msdn.com/b/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx)。 - Jason Watkins
2个回答

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();
希望我保持简单易懂的同时没有错过重点 :)

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接