Как использовать список элементов (унаследованный от абстрактного класса) в качестве его абстрактного класса?

#c# #generics #abstract-class #abstraction

#c# #обобщения #абстрактный класс #абстракция

Вопрос:

Вот краткий пример проблемы, с которой я сталкиваюсь

У меня есть два абстрактных класса EntityAbs и ListEntityAbs :

 public abstract class EntityAbs {
    //Save itself in a Database
    public abstract int SaveEntity();
}
public abstract class ListEntitiesAbs<T> where T : EntityAbs {
    protected List<T> InnerList;

    //Save each values in a Database
    public virtual int SaveEntities()
    {
        return InnerList.Sum(entity => entity.SaveEntity());
    }
}
 

Несколько классов, которые наследуют EntityAbs и получают свои собственные ListEntities (пример с классом с именем Item) :

 public class Item : EntityAbs {
    public override int SaveEntity()
    {
        // Implements here how I save it
    }
}
public class Items : ListEntitiesAbs<Item>, IList<Item> { 
    // Has specific class stuff in it
}
 

Теперь я хотел бы сохранить все ListEntitiesAbs где-нибудь еще в моей базе данных

 public int Save(params ListEntitiesAbs<EntityAbs>[] lists)
{
    for(int i = 0; i < lists.Length; i  )
        lists[i].SaveEntities();
}

Items items = new Items();
// add items 
int result = Save(items);
 

Но тогда элементы (или любой другой список, который я добавляю сюда) отображают ошибку Visual Studio, и я не понимаю, почему.
Ошибка говорит, что это Cannot convert from Items to ListEntitiesAbs<EntityAbs> , но элементы наследуются из ListEntitiesAbs и Item из EntityAbs..

Я что-то упускаю? Я пробовал использовать ListEntityAbs или использовать Cast<>, но ничего не подходит

Любая помощь приветствуется, поскольку я даже не уверен, действительно ли такое поведение возможно

Ответ №1:

Well ListEntitiesAbs<T> не является ковариантным. Это означает, что ListEntitiesAbs<Item> это не так и не может быть тривиально преобразовано в, ListEntitiesAbs<EntityAbs> .

Вариант 1: сделать Save общий

 public int Save<T>(params ListEntitiesAbs<T>[] lists) where T:EntityAbs 
{
    for(int i = 0; i < lists.Length; i  )
       lists[i].SaveEntities();
}
 

Вариант 2: не универсальный интерфейс. Вы также могли бы создать ковариантный интерфейс, но, похоже, в данном конкретном случае это не требуется.

 public interface IListEntitiesAbs {
    public int SaveEntities();
}
public int Save(params IListEntitiesAbs[] lists){
    ...
}
 

Комментарии:

1. Вариант 1 — это именно то, что мне было нужно, и мне стыдно, что я не подумал об этом. Это решает мою проблему совместимости, но, params похоже, не работает, если я добавляю несколько списков при вызове Save() метода, у вас есть какие-либо другие подсказки?

2. @arrcival params должен отлично работать с универсальными методами. Можно предположить, что типы не совпадают, т. Е. Вы пытаетесь вызвать Save(new ListEntitiesAbs<Item>(), new ListEntitiesAbs<EntityAbs>()) это, но это не сработает по причине, указанной выше. Если это проблема, используйте вместо этого вариант 2.

3. Это скрипка .NET, основанная на моем примере dotnetfiddle.net/eqPglu . Я думаю, ни одно из этих решений не работает, и мне придется выбрать вариант 2