用于将类型对象添加到拟合泛型类型的对象的通用解决方案



我觉得像界面(反对?方差是答案,但找不到正确的解决方案。

让我们有这些类:

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;
}
}

然后,只需要求每个Covariantpicture设置fruit,如果可以:

foreach (var fruit in myFruits) {
foreach (var picture in myPictures) {
if (picture is Covariant cov)
cov.SetCompatibleFruit(fruit);
}
}

最新更新