泛型类型的强制转换失败

时间:2017-04-12 19:35:21

标签: c# generics casting

我无法将泛型类型转换为另一种泛型类型,除了强制转换应该是有效的

我想归档的内容是简短的(MyModel实施IModelMyImplementation实施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>

  • 为什么演员会失败?这有额外的资源吗?它与Invalid Cast of Type Constrained C# Generic有类似的问题,但没有解释为什么只是它不起作用。
  • 如果无法进行此类演员,那么归档与上述字典类似的功能的最佳方法是什么?

2 个答案:

答案 0 :(得分:6)

虽然Olivier的回答可以解释为什么这通常会出错,但是有一种方法可以在你的程序中使用它。

您想要的功能称为通用接口协方差。如果CatAnimal,则协方差是IFoo<Cat>IFoo<Animal>的属性。

C#中的协方差仅适用于以下情况:

  • “外部”类型是接口,委托或数组。没有类别或结构。
  • 如果是接口或委托,则必须在编译时将该类型标记为支持协方差。数组免费获得(不安全!)协方差。
  • “内部”类型 - 变化的类型 - 都是引用类型。即使IFoo<int>IFoo<object>,您也不能说intobject,因为它们不是两种参考类型。

要将接口标记为协变,请在声明允许变化的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} } {} CatCat

但在第二种情况下,我们的情况恰恰相反。如果我们允许将Animal转换为I<Cat>,那么我们可以使用I<Animal> M,但真正的实现只能处理Turtle个。这就是为什么C#会使这个非法。

所以前进并使用协方差,但请记住你必须向编译器证明你想要它,并且它在所有情况下都是安全的。如果你不想要它,或者它不安全,那么你就没有协方差,你必须找到解决问题的不同方法。

答案 1 :(得分:4)

这种转换不允许有充分的理由。让我们举一个问题更明显的例子。我们有课程AnimalCat : AnimalDog : 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#中不兼容,即使AB也是如此!

你需要另一种方法。

您可以做的是使用具有泛型类型约束的泛型方法将字典包装在类中。

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);
    }

    ...
}