我无法将泛型类型转换为另一种泛型类型,除了强制转换应该是有效的
我想归档的内容是简短的(MyModel
实施IModel
,MyImplementation
实施IImplementation
):
IImplementation<IModel> implementation = new MyImplementation<MyModel>();
Assert.IsNull(implementation as IImplementation<IModel>);
这有点令人困惑,因为类型应该有效。
完整的概念模型:
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>;
}
我需要使用此广告素材才能将多个IImplementation
保存到同一个Dictionary<Type, IImplementation<IModel>>
,其中密钥是通过typeof(TModel)
获得的。
要做到这种类型安全,我不想使用Dictionary<Type, object>
。
答案 0 :(得分:6)
虽然Olivier的回答可以解释为什么这通常会出错,但是有一种方法可以在你的程序中使用它。
您想要的功能称为通用接口协方差。如果Cat
是Animal
,则协方差是IFoo<Cat>
是IFoo<Animal>
的属性。
C#中的协方差仅适用于以下情况:
IFoo<int>
是IFoo<object>
,您也不能说int
是object
,因为它们不是两种参考类型。要将接口标记为协变,请在声明允许变化的type参数之前放置out
:
interface IImplementation<out TModel> where TModel : IModel { }
如果你这样做,你的程序将开始工作。
但,out
提醒您,只有在输出位置使用T
时,协方差才是安全的。这是合法的:
interface I<out T> {
T M();
}
这不是:
interface I<out T> {
void M(T t);
}
在第一个中,T只传递 out 。在第二个中,它在中传递。
在第一种情况下,我们不能使用协方差来引入类型洞。我们有I<Cat>
,我们将其转换为I<Animal>
,现在M
返回Animal
,但是没关系,因为我们已经知道它会返回{{1} } {} Cat
是Cat
。
但在第二种情况下,我们的情况恰恰相反。如果我们允许将Animal
转换为I<Cat>
,那么我们可以使用I<Animal>
M
,但真正的实现只能处理Turtle
个。这就是为什么C#会使这个非法。
所以前进并使用协方差,但请记住你必须向编译器证明你想要它,并且它在所有情况下都是安全的。如果你不想要它,或者它不安全,那么你就没有协方差,你必须找到解决问题的不同方法。
答案 1 :(得分:4)
这种转换不允许有充分的理由。让我们举一个问题更明显的例子。我们有课程Animal
,Cat : Animal
和Dog : Animal
。现在让我们这样做:
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.
但是等等!这份名单实际上是猫的名单!我们正试图添加一只狗。即使将new Animal()
添加到list
(静态类型为List<Animal>
)也行不通。
因此,T<A>
和T<B>
两种类型在C#中不兼容,即使A
和B
也是如此!
你需要另一种方法。
您可以做的是使用具有泛型类型约束的泛型方法将字典包装在类中。
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);
}
...
}