#c# #.net #ienumerable
#c# #.net #ienumerable
Вопрос:
Я хочу сделать что-то вроде этого:
class T : IEnumerable<string>, IEnumerable<int>
{
string [] _strings = new string [20];
int[] _ints = new int[20];
public T() { }
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
foreach (string str in _strings)
yield return str;
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
foreach (int i in _ints)
yield return i;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
//Using in code:
T t = new T();
foreach (int i in t)
//to do something
foreach (string str in t)
//to do stuff
Я хочу знать, есть ли способ реализовать это или нет. Может быть, есть хитрости?
Ответ №1:
Вам почти удалось реализовать оба интерфейса — вам просто нужно изменить не общую реализацию, чтобы показать, какую общую реализацию вы пытаетесь делегировать. Например:
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<int>)this).GetEnumerator();
}
Однако, поскольку вы реализуете более одного IEnumerable<T>
интерфейса, вам нужно будет выполнить приведение в цикле foreach, чтобы показать, какой из них использовать:
T t = new T();
foreach (int i in (IEnumerable<int>) t)
//to do something
foreach (string str in (IEnumerable<string>) t)
Лично я бы настоятельно не советовал этого делать, если это возможно — это вызовет много путаницы у людей, читающих ваш код.
Подробности о том, как компилятор обрабатывает выражение для итерации в цикле, см. В разделе 8.8.4 спецификации языка C # foreach
.
(Используя «обычную» реализацию интерфейса для одного из интерфейсов, вы могли бы предоставить своего рода «default» — но я не думаю, что это действительно улучшило бы ситуацию.)
Ответ №2:
Я бы предложил определить некоторые типы структур, единственным полем которых является экземпляр вашего типа класса, инициализируемый конструктором stuct , единственным открытым членом которого будет GetEnumerator , и который реализует IEnumerable<whatever> , вызывая соответствующий метод для вашего базового типа. Затем пусть ваш корневой класс реализует член, который возвращает новый экземпляр соответствующей структуры.
Например, если в вашем классе Foo был метод GetStringEnumerator, который возвращал тип, реализующий IEnumerable<String> , у вас может быть Foo .AsStrings возвращает структуру FooStringEnumerable, метод GetEnumerator которой называется Foo .GetStringEnumerator .
Обратите внимание, что, если ваша перечисляемая вещь будет структурой, а не классом, вы избежите необходимости создавать дополнительный экземпляр объекта в обычном случае, когда он используется в цикле «для каждого», поскольку и vb, и c # будут вводить метод GetEnumerator вместо приведения к IEnumerableили IEnumerable<T> .