为什么我不能在使用通用约束时将具体类型转换为接口

时间:2016-04-11 20:47:18

标签: c# .net generics

我有一个类似下面的代码结构

interface IAnimal 
{
    string name;
    int Age;
}

class Tiger : IAnimal
{
    public string name;
    public int Age;
}

class Dog : IAnimal
{
    public string name;
    public int Age;
}

扩展类位于

之下
public static class AnimalExtension
{
    public static TAnimalType ConvertTo<TAnimalType>(this Animal animal) where TAnimalType : IAnimal
    {
        TAnimalType concreteAnimal;
        if (animal.Name == "tom")
        {
            concreteAnimal = new Tiger(); // compiler error at this line
        }
        return concreteAnimal;

    }
}

错误:无法将Tiger隐式转换为TAnimal

为什么编译器无法将Tiger(此类实现的IAnimal)转换为TAnimal。使用通用约束时,这似乎是一个问题。

虽然下面的代码工作正常..

IAnimal myTiger = new Tiger();

3 个答案:

答案 0 :(得分:4)

因为TAnimalType可以是任何动物类型,甚至Dog。如果有人致电ConvertTo<Dog>(),您编写的代码将如下所示:

public static Dog ConvertTo(this Animal animal)
{
    Dog concreteAnimal;
    if (animal.Name == "tom")
    {
        concreteAnimal = new Tiger(); // compiler error at this line
    }
    return concreteAnimal;
}

用户要求Dog,但您尝试分配Tiger并将其返回。您无法做到这一点,因为Tiger不是Dog

答案 1 :(得分:4)

你有:

class Tiger : IAnimal

和其他地方的约束:

where TAnimalType : IAnimal

然后你去:

TAnimalType concreteAnimal;
// ...
concreteAnimal = new Tiger(); // compiler error at this line

但是我们(和编译器)知道的是TigerTAnimalType都实现了相同的接口。 这并不意味着TigerTAnimalType之间存在任何关系!

例如,TAnimalType可以合法地成为Dog。在这种情况下,您会将Tiger放入Dog变量concreteAnimal。这是不允许的。

答案 2 :(得分:1)

  

为什么编译器无法将Tiger(此类实现的IAnimal)转换为TAnimal。使用通用约束时,这似乎是一个问题。

我认为其他答案已解决为什么您的代码无法按照书面形式工作。但是,这是一个如何完成我认为之后的例子。使用此扩展方法,调用者可以明确说明要将*转换为* IAnimal的实现。 TOut参数受约束,因此它具有无参数构造函数,因此我可以创建它的新实例并复制原始animal实例中的值。如果有的话,这可能会给你一些思考的食物。希望有所帮助!

*单词&#34;转换&#34;这里有点误导。我们绝对不会在这里转换任何东西。

class Program
{
    static void Main(string[] args)
    {
        Dog d = new Dog { Age = 37, Name = "Nick" };
        Tiger t = d.ConvertTo<Dog, Tiger>();
        Debug.Assert(t.Name == d.Name);
        Debug.Assert(t.Age == d.Age);
    }
}

static class AnimalExtension
{
    public static TOut ConvertTo<TIn, TOut>(this TIn animal) 
        where TIn : IAnimal 
        where TOut : IAnimal, new()
    {
        TOut convertedAnimal = new TOut
        {
            Age = animal.Age,
            Name = animal.Name
        };

        return convertedAnimal;
    }
}

interface IAnimal
{
    string Name { get; set; }
    int Age { get; set; }
}

class Tiger : IAnimal
{
    public int Age { get; set; }
    public string Name { get; set; }
}

class Dog : IAnimal
{
    public int Age { get; set; }
    public string Name { get; set; }
}