Универсальное решение для добавления объекта типа к объекту подходящего универсального типа
Я чувствую, что интерфейс (наоборот?) - это ответ, но я не могу найти правильное решение.
Давайте иметь эти классы:
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;
}
}
Тогда просто спросите каждого Covariant
picture
установить fruit
если может:
foreach (var fruit in myFruits) {
foreach (var picture in myPictures) {
if (picture is Covariant cov)
cov.SetCompatibleFruit(fruit);
}
}