#c# #oop
#c# #ооп
Вопрос:
Поэтому я думаю, что, возможно, я слишком усложняю свою реализацию, но вот что я делаю:
- Я хочу, чтобы моя база данных была представлена IRepository
- SqlRepository — это конкретная реализация IRepository.
- Я хочу, чтобы все таблицы в базе данных были представлены с помощью ITable.
- У меня есть таблица с именем Items, которая представлена ItemsTable .
- Каждая запись в таблице элементов представлена IItem .
- Item — это конкретная реализация IItem.
Теперь моя проблема в том, что в моей программе, когда я хочу использовать элементы списка = репозиторий.Товары.List(); он не будет компилироваться, потому что в ItemsTable реализация ITable возвращает IList, а не List . Я мог бы вернуть IList, но я хочу работать с конкретным списком элементов.
Что я могу сделать лучше?
public interface ITable
{
IList<IItem> List();
bool Add(IItem item);
bool Delete(long itemId);
bool Find(long itemId);
}
public class ItemsTable : ITable
{
public IList<IItem> List()
{
IList<IItem> items = GetItems(); // GetItems return List<Item>, Item implements IItem.
return items;
}
.....
...
..
}
public class SQLRepository : IRepository
{
ITable items = new ItemsTable();
public ITable Items
{
get { return items; }
}
}
static void Main(string[] args)
{
var repository = new SQLRepository();
List<Item> items = repository.Items.List();
}
Комментарии:
1. Почему этот вопрос помечен как c #, так и Java?
2. Есть ли у вас какие-либо проблемы с работой IList<T> на данный момент?
3. Я думаю, что это скорее вопрос дизайна, а не вопрос, специфичный для c #, если бы я кодировал на Java, у меня был бы тот же вопрос.
4. @zenwalker Да, я не хочу работать с IList<T>, я хочу работать с List<Item>, потому что Item предоставляет дополнительные методы, которых нет у IItem.
Ответ №1:
У меня есть пара комментариев для вас.
Во-первых, только потому, что вы определяете интерфейсы, которые сопоставляются с вашими конкретными классами, вам не нужно использовать интерфейсы везде. Смысл использования списка интерфейсов, который вы используете, заключается в ограничении площади поверхности, с которой могут играть пользователи вашего репозитория и объектов. Так что не стесняйтесь возвращаться List<T>
, а не IList<T>
.
Во-вторых, однако возврат всего, что реализует IList<T>
, подразумевает, что вы хотите изменить содержимое списка после того, как получите его из своего репозитория. Теперь это может быть плохо и плохо по ряду причин.
Если ваш репозиторий хранит ссылки на возвращаемые им списки, то последующие вызывающие пользователи могут получить ваши измененные списки — так что вы действительно не можете сохранять ссылки. Итак, вам нужно создавать списки для каждого вызова.
Если вы создаете список для каждого вызова, то вы фактически вызываете .ToList
оператора перед возвратом результатов.
Альтернатива состоит в том, чтобы позволить вызывающему абоненту решать, когда он звонит .ToList
, а затем вы можете просто вернуть an IEnumerable<T>
.
Теперь это лучше во многих отношениях.
Это позволяет вызывающему коду знать, что список не был изменен другим вызывающим абонентом. И это также позволяет списку запрашивать себя для каждой новой итерации списка.
Итак, моя рекомендация — не возвращайте IList<T>
или List<T>
— return IEnumerable<T>
вместо этого и позвольте вызывающему абоненту позвонить .ToList()
.
Комментарии:
1. Я согласен с использованием IEnumerable. Это то, что я в итоге сделал, но вопрос касался ошибки компилятора, поэтому я принял другой ответ.
Ответ №2:
Лучшим было бы изменить ваш интерфейс на:
public interface ITable<TItem>
where TItem : IItem
{
IList<TItem> List();
bool Add(TItem item);
bool Delete(long itemId);
bool Find(long itemId);
}
Комментарии:
1. Я планирую иметь дополнительные реализации IItem, кроме Item
2. Я нигде не использовал свое имя ответа
Item
. Таким образом, вы можете использовать там все, что реализуетIItem
интерфейс.3. Разве этот ответ не сводит на нет всю цель использования интерфейсов для абстрагирования базовых конкретных классов?
4. Нет, потому что с помощью интерфейса вы можете использовать свойства и методы, определенные с помощью интерфейса. Без этого элементы будут рассматриваться как
objects
Ответ №3:
Теперь моя проблема в том, что в моей программе, когда я хочу использовать элементы списка = репозиторий.Товары.List(); он не будет компилироваться, потому что в ItemsTable реализация ITable возвращает IList, а не List . Я мог бы вернуть IList, но я хочу работать с конкретным списком элементов.
Хорошо. У вас не может быть обоих. Если вы хотите работать с a List<T>
, вам нужно вернуть его. Вы не можете создать контракт, в котором говорится, что любая IList<T>
реализация в порядке, а затем ожидать, что a List<T>
будет возвращено.
Такого рода нарушения обычно приводят к путанице в обслуживании в будущем, поскольку IList<IItem> List();
обещают одно, а вы ожидаете, что оно доставит что-то другое. Это все равно, что пойти на заправку и заказать неэтилированный газ и ожидать этилированный газ (это все еще газ, но с небольшим количеством светодиодов).
Ответ №4:
В .NET нет различий для общих коллекций.
Реализуйте метод интерфейса явно и обеспечьте перегрузку, которая возвращает конкретный список:
public MyTable : ITable
{
IList<IItem>ITable List()
{
return List().OfType<IItem>().ToList();
}
public List<MyItem> List()
{
var l = new List<MyItem>();
...
return l;
}
}