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>
可以证明没有可以将长颈鹿插入序列的方法。
提前谢谢你。这是我的代码:
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>
可以证明没有可以将长颈鹿插入序列的方法。