Использование IEnumerable без цикла foreach
Я должен пропустить что-то простое здесь.
Возьмите следующий код:
public IEnumerable<int> getInt(){
for(int i = 0; i < 10; i++){
yield return i;
}
}
Я могу назвать это с:
foreach (int j in obj.getInt()){
//do something with j
}
Как я могу использовать метод getInt без цикла foreach:
IEnumerable<int> iter = obj.getInt();
// do something with iter ??
Благодарю.
редактирует
Для тех, кто интересуется, почему я хочу этого. Я повторяю две вещи:
IEnumerator<int> iter = obj.getInt().GetEnumerator();
foreach(object x in xs){
if (x.someCondition) continue;
iter.MoveNext();
int n = iter.current();
x.someProp = n;
etc...
}
6 ответов
Вы можете получить ссылку на Enumerator
, с использованием GetEnumerator
метод, то вы можете использовать MoveNext()
метод двигаться дальше, и использовать Current
свойство для доступа к вашим элементам:
var enumerator = getInt().GetEnumerator();
while(enumerator.MoveNext())
{
int n = enumerator.Current;
Console.WriteLine(n);
}
Мой совет: не связывайтесь с счетчиками вообще. Охарактеризуйте свою проблему как последовательность операций над последовательностями. Напишите код для выражения этих операций. Пусть операторы последовательности позаботятся об управлении перечислителями.
Итак, давайте посмотрим, понял ли я это прямо. У вас есть две последовательности. Допустим, { 2, 3, 5, 7, 12 } и { "лягушка", "жаба" }. Логическая операция, которую вы хотите выполнить, это сказать: "пройти через первую последовательность. Каждый раз, когда вы найдете число, делимое на три, получите следующий элемент во второй последовательности. Сделайте что-нибудь с полученной парой (число, амфибия)".
Легко сделано. Сначала отфильтруйте первую последовательность:
var filtered = firstSequence.Where(x=>x%3 == 0);
Затем, отфильтруйте отфильтрованную последовательность со второй последовательностью:
var zipped = filtered.Zip(
secondSequence,
(y, z)=> new {Number = x, Amphibian = y});
И теперь вы можете перебирать заархивированную последовательность и делать с парами все, что захотите:
foreach(var pair in zipped)
Console.WriteLine("{0} : {1}", pair.Number, pair.Amphibian);
Легко peasy, не возиться с счетчиками.
Как насчет этого?
IEnumerator<int> iter = obj.getInt();
using(iter) {
while(iter.MoveNext()) {
DoSomethingWith(iter.Current)
}
}
Использование для цикла:
for (var enumerator = getInt().GetEnumerator(); enumerator.MoveNext(); )
{
Console.WriteLine(enumerator.Current);
}
Хотя принятые ответы верны, обратите внимание, что IEnumerator.Current не определен перед первым вызовом MoveNext().
Если вы перебираете вторичный массив, вам нужно что-то вроде:
IEnumerable<Foo> Foo() { ... }
int AssignValues(List<TakesFoo> data) {
var count = 0;
var foo = Foo().GetEnumerator();
// Step into the first element of the array;
// doing this does not discard a value from the IEnumerator
if (foo.MoveNext()) {
foreach (var x in data) {
x.SetFoo(foo.Current);
count += 1;
if (!foo.MoveNext()) {
break;
}
}
// Return count of assigned values
return count;
}
Важно отметить, что долг foreach
Цикл должен располагать перечислитель, если экземпляр реализует IDisposable
, Другими словами, foreach
следует заменить на что-то вроде:
var enumerator = enumerable.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
var item = enumerator.Current;
// do stuff
}
}
finally
{
var disposable = enumerator as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
Образец,
public static void SampleIteratorNext()
{
var beauty = BeautifulLadies().GetEnumerator();
Console.WriteLine($"Friday with {Next(beauty)} ...");
Console.WriteLine($"Saturday with {Next(beauty)} ...");
Console.WriteLine($"Tusday with {Next(beauty)} ...");
}
public static IEnumerable<string> BeautifulLadies()
{
yield return "Scarlett";
yield return "Alexandra";
yield return "Alisson";
}
// emulate next() in python
public static T Next<T>(IEnumerator<T> iterator)
{
iterator.MoveNext();
return iterator.Current;
}