[英]cast of generic type fails
I am unable to cast a generic type to another generic type, besides the cast should be valid我无法将泛型类型转换为另一种泛型类型,除了转换应该是有效的
What I want to archive is in short (for MyModel
implementing IModel
, and MyImplementation
implementing IImplementation
):我想存档是在短期(用于MyModel
实施IModel
和MyImplementation
实施IImplementation
):
IImplementation<IModel> implementation = new MyImplementation<MyModel>();
Assert.IsNull(implementation as IImplementation<IModel>);
This is a bit confusing, as the type should be valid.这有点令人困惑,因为类型应该是有效的。
Complete conceptual model:完整的概念模型:
interface IModel {}
class MyModel : IModel {}
interface IImplementation<TModel> where TModel : IModel { }
class MyImplementation<TModel> : IImplementation<TModel>
where TModel : IModel { }
public void CallRegister()
{
var implementation = new MyImplementation<MyModel>();
var instance = CastModel(implementation);
Assert.IsNotNull(instance); //this assert fails!
}
private object CastModel<TModel>(IImplementation<TModel> implementation) where TModel : IModel
{
return implementation as IImplementation<IModel>;
}
I need this cast to enable me to save multiple IImplementation
s to the same Dictionary<Type, IImplementation<IModel>>
, where the key is obtained by doing typeof(TModel)
.我需要这个转换来让我能够将多个IImplementation
保存到同一个Dictionary<Type, IImplementation<IModel>>
,其中的键是通过typeof(TModel)
。 To do this type safe I don't want to use a Dictionary<Type, object>
.为了做到这种类型安全,我不想使用Dictionary<Type, object>
。
Though Olivier's answer gets the idea across about why this usually goes wrong, there is a way to make this work in your program.尽管 Olivier 的回答说明了为什么这通常会出错,但有一种方法可以在您的程序中实现这一点。
The feature you want is called generic interface covariance .您想要的功能称为通用接口协方差。 Covariance is the property that if a Cat
is an Animal
, then an IFoo<Cat>
is an IFoo<Animal>
.协方差是一个属性,如果Cat
是Animal
,则IFoo<Cat>
是IFoo<Animal>
。
Covariance in C# only works in the following situations: C# 中的协方差仅适用于以下情况:
IFoo<int>
is an IFoo<object>
even though an int
is an object
, because they are not both reference types.即使int
是object
,也不能说IFoo<int>
是IFoo<object>
,因为它们不是两种引用类型。 To mark an interface as covariant, you put out
before the declaration of the type parameter which you wish to allow to vary:要标记的接口协变,你把out
你希望允许改变类型参数的声明之前:
interface IImplementation<out TModel> where TModel : IModel { }
If you do that, your program will start to work.如果你这样做,你的程序就会开始工作。
HOWEVER , out
is a reminder to you that covariance is only safe if T
is used in output positions.但是, out
提醒您,只有在输出位置使用T
,协方差才是安全的。 This is legal:这是合法的:
interface I<out T> {
T M();
}
This is not:这不是:
interface I<out T> {
void M(T t);
}
In the first, T is only passed out of things.在第一种情况下,T 只是从事物中传递出去。 In the second, it is passed in .在第二个,它被传递。
In the first scenario, we cannot use covariance to introduce a type hole.在第一种情况下,我们不能使用协方差来引入类型漏洞。 We have an I<Cat>
and we cast it to I<Animal>
, and now M
returns an Animal
, but that's OK, because we already know that it will return a Cat
, and a Cat
is an Animal
.我们有一个I<Cat>
并将它转换为I<Animal>
,现在M
返回一个Animal
,但没关系,因为我们已经知道它会返回一个Cat
,而Cat
是一个Animal
。
But in the second scenario, we have the opposite situation.但在第二种情况下,我们有相反的情况。 If we allowed an I<Cat>
to be converted to I<Animal>
then we have an M
that can take a Turtle
, but the real implementation can only handle Cat
s.如果我们允许将I<Cat>
转换为I<Animal>
那么我们就有一个可以接受Turtle
的M
,但真正的实现只能处理Cat
。 That's why C# will make this illegal.这就是为什么 C# 将使其成为非法的原因。
So go forth and use covariance, but remember that you have to certify to the compiler that you want it, and that it is safe under all circumstances .所以继续使用协方差,但请记住,您必须向编译器证明您想要它,并且它在任何情况下都是安全的。 If you don't want it, or it is not safe, then you don't get to have covariance, and you'll have to find a different solution to your problem.如果你不想要它,或者它不安全,那么你就没有协方差,你必须为你的问题找到不同的解决方案。
This kind of conversion is not allowed for good reasons.出于充分的理由,不允许进行这种转换。 Let's take an example where the problem is more obvious.让我们举一个问题更明显的例子。 We have the classes Animal
, Cat : Animal
and Dog : Animal
.我们有类Animal
、 Cat : Animal
和Dog : Animal
。 Now let's do this:现在让我们这样做:
List<Animal> list = new List<Cat>(); // Seems to be possible at first glance.
// An now comes the problem:
list.Add(new Dog()); // Seems to be possible as well.
But wait!可是等等! The list is in reality a list of cats!该列表实际上是一个猫列表! And we are trying to add a dog.我们正在尝试添加一只狗。 Even adding new Animal()
to list
, which is statically typed as List<Animal>
, would not work.即使添加new Animal()
到list
,这是静态类型的List<Animal>
,是行不通的。
Therefore two types T<A>
and T<B>
are not assignment compatible in C#, even if A
and B
are!因此,两种类型T<A>
和T<B>
在 C# 中不是赋值兼容的,即使A
和B
是!
You need another approach.你需要另一种方法。
What you can do is to wrap your dictionary in a class with a generic method having a generic type constraint.您可以做的是使用具有泛型类型约束的泛型方法将字典包装在一个类中。
public class MyImplementationDict
{
private readonly Dictionary<Type, object> _internalDict = new Dictionary<Type, object>();
public void Add<T>(IImplementation<T> item)
where T : IModel
{
_internalDict.Add(typeof(T), item);
}
...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.