【问题标题】:Casting generic with inner type as interface使用内部类型作为接口转换泛型
【发布时间】:2022-01-21 10:49:10
【问题描述】:

.NET Fiddle

using System;

interface Interface { }
class Outer<T> : Interface
    where T : Interface { }
class Inner : Interface { }

public class Program
{
    public static void Main()
    {
        Type outer = typeof(Outer<>);
        Type inner = typeof(Inner);
        Type expectedType = typeof(Outer<Interface>);
        Type final = outer.MakeGenericType(inner);

        // This works
        Inner innerInstance = (Inner)Activator.CreateInstance(inner);

        // This works
        Outer<Inner> outerInstance = (Outer<Inner>)Activator.CreateInstance(final);

        // We can cast the real type
        Interface interfaceInstance = innerInstance;

        // But we cant cast with the interface as the inner type
        Outer<Interface> casted = (Outer<Interface>)outerInstance;
    }
}

此转换将失败 (Outer&lt;Interface&gt;)outerInstance;,因为它无法将内部类型识别为接口。
有什么方法可以强制或将其转换为Outer&lt;Interface&gt; 而不是Outer&lt;Inner&gt;

【问题讨论】:

  • 没有。您要求的是通用方差,它不适用于类。

标签: c# .net generics


【解决方案1】:

我重命名了这些类型,因为名为 InterfaceInnerOuter&lt;T&gt; 的类型扰乱了我的大脑。希望这能让类型之间的关系更容易看到。

interface IAnimal { }

class Dog : IAnimal { }

class PetStore<T> : IAnimal where T : IAnimal
{}

当我们简化它时,问题是为什么编译器不允许我们这样做?

PetStore<IAnimal> casted = new PetStore<Dog>(); 

每个Dog 都是IAnimal,那我们为什么不能这样做呢?

总有一些东西是编译器保护我们的。有时很难弄清楚那是什么。

如果我们在PetStore&lt;T&gt; 和另一个类中添加一点点就会变得更清楚:

class PetStore<T> : IAnimal where T : IAnimal
{
    private List<T> _pets = new List<T>();

    public void Add(T foo)
    {
        _pets.Add(foo);
    }
}

class Cat : IAnimal { }

如果我们有一个PetStore&lt;Dog&gt; 的实例,那么内部列表包含Dog 类型的项目。我们添加到列表中的每一项都必须是 Dog 类型。

如果我们可以将其转换为PetStore&lt;IAnimal&gt;,那么我们可以这样做:

var dogStore = new PetStore<Dog>();
PetStore<IAnimal> animalStore = (PetStore<IAnimal>)dogStore;
animalStore.Add(new Cat())

现在我们可以看到编译器正在阻止什么。 dogStorePetStore&lt;Dog&gt;_pets 列表中的每一项都必须是 Dog

但是通过将对象转换为PetStore&lt;IAnimal&gt;,我们可以将Cat 添加到列表中,因为CatIAnimal。那没有任何意义。这是一个List&lt;Dog&gt;,那么我们怎么能在列表中添加不是Dog 的东西呢?


这是一个更简单的版本,可以用更少的噪音说明问题:

var dogs = new List<Dog>();
var animals = (List<IAnimal>)dogs; // this won't compile.
animals.Add(new Cat());

我们可能在某个时候尝试过这样做。出于完全相同的原因,编译器不允许这样做。它可以让我们创建一个List&lt;Dog&gt;,然后向它添加一些不是Dog的东西。

我们可能会推断我们永远不会尝试将Cat 添加到列表中,因为我们知道它应该只包含狗。但在这种情况下,为什么首先将List&lt;Dog&gt; 转换为List&lt;IAnimal&gt;

另外,如果我们有这样的方法呢:

void DoSomethingWithListOfAnimals(List<IAnimal> animals)

如果我们可以将List&lt;Dog&gt; 转换为List&lt;IAnimal&gt;,那么我们可以将它作为参数传递给这个方法。这个方法中的代码无法知道这个列表应该只包含狗。它只知道它有一个List&lt;IAnimal&gt;

如果我们能够理解为什么编译器不会让我们做某事,这将很有帮助。但出于实际目的,编译器总是正确的。如果它不允许我们做某事,那是有原因的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2011-01-07
    • 2019-09-16
    相关资源
    最近更新 更多