Универсальное решение для добавления объекта типа к объекту подходящего универсального типа

Я чувствую, что интерфейс (наоборот?) - это ответ, но я не могу найти правильное решение.

Давайте иметь эти классы:

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>(),
    };
    

Теперь я стремлюсь сделать очень простую вещь, но разносторонним образом, то есть хочу избегать любых переключателей, если мне придется менять код каждый раз, когда обнаруживается новый фрукт и в коллекции может появиться новый 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?
        }
    }
}

Спасибо, господин. Скит (шутит, вроде)

1 ответ

Решение

Поскольку проблема заключается в том, что вы хотите обеспечить безопасность типов во время компиляции, но не знаете типы до времени выполнения, вы можете отложить решение до времени выполнения, используя 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);
    }
}
Другие вопросы по тегам