我觉得像界面(反对?方差是答案,但找不到正确的解决方案。
让我们有这些类:
public abstract class Fruit { }
public class Banana : Fruit { }
public class Apple : Fruit { }
public abstract class Picture { }
public class FruitPicture<T> : Picture, Contravariant<T>, Covariant<T> where T : Fruit
{
T myFruit = null;
public Type GetFruitType() { return typeof(T); }
public void AddFruit(T fruit) { this.myFruit = fruit; }
}
public interface Contravariant<in T> { void AddFruit(T model); }
public interface Covariant<out T> { Type GetFruitType(); }
我的情况是:
我已经初始化了香蕉和苹果的集合,例如这两个(但我可以使用不同的(:
Fruit[] myFruits = new Fruit[2] { new Banana(), new Apple() };
我有一个图片集合,例如这两个:
Picture[] myPictures = new Picture[2] { new FruitPicture<Banana>(), new FruitPicture<Apple>(), };
现在,我试图做一件非常简单的事情,但以一种通用的方式,这意味着我想避免任何开关/ifs,每次发现新水果时我都必须更改代码,并且新的FruitPicture可能会出现在集合中=>我想从我的集合.AddFruit()
到正确类型的FruitPicture。我几乎可以更改任何逻辑,但我想保留通用的 FruitPicture 类。
我得到的最接近的是:
foreach(Picture curPicture in myPictures)
{
foreach (Fruit curFruit in myFruits)
{
Covariant<Fruit> fruitType = (Covariant<Fruit>)curPicture;
if (curFruit.GetType() == fruitType.GetFruitType())
{
// what now?
}
}
}
谢谢斯基特先生(开玩笑;有点(
由于您遇到的问题是您想执行编译时类型安全,但直到运行时才知道类型,因此您可以使用dynamic
将决定推迟到运行时。我不一定推荐这个,只是说它会起作用。
我将Covariant
接口更改为不需要泛型接口,因为它没有使用类型参数。我将AddFruit
重命名为SetFruit
,因为它没有添加任何内容,而是替换了。
foreach (var fruit in myFruits) {
foreach (var picture in myPictures) {
if (picture is Covariant cov) {
if (cov.GetFruitType() == fruit.GetType())
((dynamic)picture).SetFruit(Convert.ChangeType((dynamic)fruit, cov.GetFruitType()));
}
}
}
需要 (dynamic
(ChangeType
,因为fruit
的类型是Fruit
,这不是传递给任何SetFruit
的有效类型。它必须是动态的,因为ChangeType
的静态编译时类型是object
,这对于任何SetFruit
都不是有效的类型。
或者,如果您将决定推入FruitPicture
怎么办?
public interface Covariant {
void SetCompatibleFruit(Fruit f);
}
public class FruitPicture<T> : Picture, Covariant where T : Fruit {
T myFruit = null;
public void SetCompatibleFruit(Fruit f) {
if (f is T tf)
this.myFruit = tf;
}
}
然后,只需要求每个Covariant
picture
设置fruit
,如果可以:
foreach (var fruit in myFruits) {
foreach (var picture in myPictures) {
if (picture is Covariant cov)
cov.SetCompatibleFruit(fruit);
}
}