Covariance Contravariance Generics Interfaces - 为什么这不起作用?

Covariance Contravariance Generics Interfaces - why doesn't this work?

提前谢谢你。这是我的代码:

public class ApplicationUser : IdentityUser
{
    public ApplicationUser()
    {

    }
}

public class AppUserManager : UserManager<ApplicationUser>
{
...
}

public interface IOwinManager
{
    UserManager<IdentityUser> UserManager { get; }
}

为什么这不起作用?

public class OwinManager : IOwinManager
{        
    public UserManager<IdentityUser> UserManager
    {
        get { return new AppUserManager(); }
    }
}

既然ApplicationUser继承自IdentityUser,AppUserManager继承自UserManager,为什么不接受组合泛型?谢谢!

不支持 类 泛型类型参数的逆变和协变。

简化您的问题:

// Compiler error!
UserManager<IdentityUser> userManager = new AppUserManager();
  • AppUserManager继承UserManager<ApplicationUser>.
  • 因此,您试图在 UserManager<IdentityUser> 引用上设置一个 UserManager<ApplicationUser> 派生的引用。这就是问题!它们是不同的类型。

OP 说...:

which, essentially means, I can't use concrete classes and their generics in an interface and expect them to be implemented by their children?

接口支持变化。因此,你可以设计你的界面如下:

public interface IOwinManager<out TUser, out TManager>
    where TUser : IdentityUser 
    where TManager : UserManager<TUser>
{
    TManager UserManager { get; }
}

...一旦你实现了这个接口,你的实现将声明一个具体 TManager 类型的 属性。

Matias 的回答很好;我想我会添加更多的上下文。让我们再次简化您的示例:

class Animal {} // IdentityUser
class Tiger : Animal {} // ApplicationUser    
class Giraffe : Animal {} // some other kind of user
class Cage<T> where T : Animal {} // UserManager
class SpecialTigerCage : Cage<Tiger> {} // AppUserManager

现在的问题是"why is this conversion illegal?"

Cage<Animal> cage = new SpecialTigerCage();

现在应该很明显为什么这是非法的了。 你可以把长颈鹿关进一个可以容纳动物的笼子里,但是如果你把长颈鹿关进一个专门的老虎笼子里,长颈鹿就不会很高兴了。

类型系统无法令人满意地证明您不会将长颈鹿放入老虎笼中,因此它不允许转换。

正如其他人指出的那样,C# 确实支持接口和委托上的这种协变转换,它可以证明你不会把长颈鹿变成老虎笼。 IEnumerable<T> 例如是协变的。在需要动物序列的地方可以使用老虎序列,因为 IEnumerable<T> 可以证明没有可以将长颈鹿插入序列的方法。