Универсальное решение для добавления объекта типа к объекту подходящего универсального типа - PullRequest
0 голосов
/ 23 октября 2018

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

Пусть у нас будут следующие классы:

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 Ответ

0 голосов
/ 23 октября 2018

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