在C#中将通用MyClass <child>转换为MyClass <parent>

时间:2016-01-31 17:15:11

标签: c# generics casting

假设我有一个父类Animal,其子类为CatDog。我还有一个Building<T>,其中T可以是Animal

public class<T> Building
{
    public T Inhabitant {get; set;}
}

现在,我想创建一些有猫的建筑物和一些有狗的建筑物(基于仅在运行时可用的参数)。为此,我正在尝试创建以下方法:

private Building<Animal> populateBuilding(string animalKind)
{
    if(animalKind == "cat")
    {
        return new Building<Cat>;
    }
    if(animalKind == "dog")
    {
        return new Building<Dog>;
    }
    return null;
}

但是,我不能这样做,因为编译器告诉Building<Dog>/Building<Cat>无法转换为Building<Animal>。我找到了几个示例,其中描述了类似的问题,但使用了列表(例如List<Animal>, List<Dog>, List<Cat>)。但是,我不会在我的情况下使用列表,并想知道它是否仍然是相同的情况。

2 个答案:

答案 0 :(得分:3)

您应该阅读一些关于协方差和逆变的文章,例如this one

在你的情况下,类Building的设计是不允许的。

假设允许共同使用Building。然后它将允许以下代码片段:

var dogHouse = new Building<Dog>();
var house = (Building<Animal>) dogHouse; // Casting to base class

house.Inhabitant = new Cat(); // Inhabitant of a Building<Animal> is any Animal
dogHouse.Inhabitant.Bark(); // Meow!

如果泛型的方法仅返回类型T的值(作为返回值或T - 参数),则可以与其类型参数out协同使用泛型。如果泛型仅接受类型T的值作为参数,则可以与其类型参数T协同使用泛型。在您的情况下,有两个,即get_Inhabitant返回Animalset_Inhabitant接受Animal

此外,在C#中,正如评论中正确指出的那样,协方差和逆变的概念仅适用于接口和代理。

答案 1 :(得分:0)

您应该能够按照以下方式执行此操作:

private Building<Animal> populateBuilding(string animalKind)
{
    var building = new Building<Animal>();

    if (animalKind == "cat")
    {
        return new Building<Animal>
        {
            Inhabitant = new Cat()
        };
    }
    if (animalKind == "dog")
    {
        return new Building<Animal>
        {
            Inhabitant = new Dog()
        };
    }

    return null;
}