3

假设我有一个小的动物继承层次结构:

public interface IAnimal {
    string Speak();
}

public class Animal : IAnimal {
    public Animal() {}
    public string Speak() {
        return "[Animal] Growl!";
    }
}

public class Ape : IAnimal {
    public string Speak() {
        return "[Ape] Rawrrrrrrr!";
    }
}

public class Bat : IAnimal {
    public string Speak() {
        return "[Bat] Screeeeeee!";
    }
}

接下来,这是一个界面,提供了一种转换stringsIAnimals.

public interface ITransmogrifier<T> where T : IAnimal {
    T Transmogrify(string s);
}

最后,这是这样做的一种策略:

public class Transmogrifier<T> : ITransmogrifier<T> where T : IAnimal, new() {
    public T Transmogrify(string s) {
        T t = default(T);
        if (typeof(T).Name == s)
            t = new T();
        return t;
    }
}

现在,问题。是否可以替换标记为 [1]、[2] 和 [3] 的部分,以便该程序能够正确编译和运行?如果不接触 [1]、[2] 和 [3] 以外的部分就无法做到这一点,您是否仍然可以从包含 IAnimal 任意实现的集合中IAnimal的每个实例中获取一个?Transmogrifier你能从一开始就形成这样一个集合吗?

    static void Main(string[] args) {
        var t = new Transmogrifier<Ape>();
        Ape a = t.Transmogrify("Ape");
        Console.WriteLine(a.Speak());  // Works!

        // But can we make an arbitrary collection of such animals?
        var list = new List<Transmogrifier< [1] >>() {
            // [2]
        };

        // And how about we call Transmogrify() on each one?
        foreach (/* [3] */ transmogrifier in list) {
            IAnimal ia = transmogrifier.Transmogrify("Bat");
        }
    }
}
4

2 回答 2

4

李是对的。

正如您的问题所暗示的那样,您可以在 C# 4 中通过在 T 中将 ITransmogrifier 标记为协变(“out”)来执行此操作。然后您可以制作 aList<ITransmogrifier<IAnimal>>并将 aTransmogrifier<Bat>放入该列表中。

于 2010-05-07T20:27:58.787 回答
3

你不能这样做,因为 and 之间没有类型关系Transmorgifier<Ape>Transmorgifier<Bat>所以你不能把它们放在同一个通用列表中(除了List<object>)。显而易见的解决方案就是使ITransmorgifier非泛型:

public interface ITransmogrifier
{
    IAnimal Transmogrify(string s);
}

那么你可以有

var list = new List<ITransmogrifier>() {
    new Transmorgifier<Ape>(), new Transmorgifier<Bat>() ...
};
于 2010-05-07T18:02:17.980 回答