#c# #.net
#c# #.net
Вопрос:
Мне пришлось реализовать 2 интерфейса одновременно с разными общими параметрами, как показано ниже. Я достаточно запутался в этом. Я понятия не имел, какой из них повторяет себя в foreach. Теперь я понимаю, что первый выбирается неявно.
Я пробовал new BarList().GetEnumerator()
, но не могу указать параметр типа на уровне метода.
Единственное решение, которое я нашел, это то, что оно преобразует его в интерфейс, подобный () new BarList() as IEnumerable<string>
После того, как вы достаточно запутались. Я просто хотел знать, что этот дизайн не очень хорошая идея? Я должен избегать реализации одного и того же общего интерфейса еще раз?
class Program
{
static void Main(string[] args)
{
foreach (var item in new BarList())
{
}
}
}
class BarList: IEnumerable<string>, IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
throw new NotImplementedException();
}
}
Редактировать:
Позвольте мне объяснить, почему я иду таким образом.
Мне пришлось реализовать IPagedList<T>
интерфейс, который унаследован от IList<T>
. Я хотел написать метод расширения, который преобразует его в мою модель представления. как показано ниже
GetAll().ToPagedList(pageindex)
;// который возвращает IPagedList, тогда я хотел использовать его, как показано ниже;
GetAll().ToPagedList(pageindex).ToViewModel<T,TViewModel>();
Для достижения этого я попытался вернуться IPagedList<ViewModel>
с помощью этого метода расширения.В этом случае я должен реализовать IPagedList 2 раза с разными параметрами. Но эта стратегия привела к путанице. Это причина этого.
Комментарии:
1. Можете ли вы опубликовать ссылку / код для IPagedList<T> или PagedList<T> ?
Ответ №1:
Это кажется немного запутанным. Почему бы не сделать явным, что происходит, добавив перечислители в качестве свойств, а не внедряя их в класс. Например,
class ProductCollection
{
public IEnumerable<int> EnumerateTheInts { get { //code to produce enumerator }}
public IEnumerable<string> EnumerateTheStringss { get { //code to produce enumerator }}
}
Не всегда плохо дважды реализовать открытый универсальный интерфейс для объекта. Например, IHandle может быть реализован классом, который может обрабатывать два типа T. Тем не менее, мне было бы сложно реализовать IEnumerable дважды, потому что вы можете не перечислять тип, который вы ожидаете, в for-each или в LINQ. Кстати, те же рассуждения для реализации более одного индексатора. Тип вашего индексатора будет определять ваш результат, который, я могу засвидетельствовать, является чрезвычайно запутанным!
Ответ №2:
Компилятор выбирает IEnumerator<int> GetEnumerator
метод, следуя правилам в 8.8.4 спецификации языка C #, которая сначала ищет доступный GetEnumerator()
метод для BarList
типа. Единственный из доступных — это возвращаемый IEnumerator<int>
.
Если бы вы также заставили этот метод использовать явную реализацию интерфейса, то он перешел бы на более поздние этапы раздела 8.8.4, в котором говорится, что если существует более одного типа T, так что происходит неявное преобразование из типа выражения ( BarList
здесь ) в IEnumerable<T>
then, возникает ошибка.
Я бы сказал, что это запутанный дизайн — я бы, вероятно, добавил свойства или методы для получения соответствующих «представлений» данных.
Ответ №3:
Я бы этого избежал. Однако это зависит от вашего использования.
Все будет в порядке, если вы просто захотите передать экземпляр в функцию, которая IEnumerable<string>
явно ожидает параметр:
- вам не придется приводить
- функция даже не будет «видеть» другие реализованные интерфейсы, поэтому путаницы нет.
YMMV
Ответ №4:
Ваш текущий дизайн сбивает с толку. Хотя вы не предоставили никакой информации о характере самой коллекции, исходя из названия, я могу предположить, что вы должны перебирать несколько продуктов. Возможно, вам следует просто иметь класс типа Product
со string
свойством и int
свойством и просто возвращать IEnumerable<Product>
вместо этого.
Таким образом, с помощью методов расширения LINQ вы можете создать IEnumerable<T>
объект, который вы на самом деле имеете в виду:
collection.Select(product => product.IntegerProperty)
collection.Select(product => product.StringProperty)
Конечно, вы также можете предоставить вспомогательные методы внутри объекта:
class ProductCollection : IEnumerable<Product> {
public IEnumerable<Product> GetEnumerator() {
// ... return Product objects here.
}
public IEnumerable<int> AsIntegerCollection() {
// yield the integer collection here
}
public IEnumerable<string> AsStringCollection() {
// yield the string collection here
}
}
Ответ №5:
Что это за коллекции строк и целых чисел? Я полагаю, что они что-то означают в связи с продуктом (например, имя, идентификатор и т. Д.), Поэтому я бы предпочел сделать что-то вроде этого:
class ProductCollection : IEnumerable<Product>
{
public IEnumerator<Product> GetEnumerator()
{
...
}
public IEnumerator<string> ProductNames // a helper to enumerate product names
{
...
}
public IEnumerator<int> ProductIds // a helper to enumerate product ids
{
...
}
}