2009-05-14 2 views
1

Так что для типа, как:Можно ли написать несколько итераторов для типа в C#?

CoolCollection<T> 

вы могли бы:

foreach (T item in coolCollection) 
{ 
    ... 
} 

foreach (CoolNode node in coolCollection) 
{ 
    ... 
} 

Если это не представляется возможным, может быть, как foreach2, или каким-либо другим способом итерацию. Часто мне хотелось бы больше, чем один способ повторения по типу.

EDIT: Извините, если не было ясно. В основном CoolNode - это узел, который делает CoolCollection. CoolNode имеет свойство, называемое значением для возврата T, но мне нужен еще один итератор, чтобы возвращать только CoolNodes.

EDIT2: Я не могу сделать coolCollection.Что-то итерации, потому что CoolNodes связаны через свойство, называемое Next, как LinkedList. Поэтому мне нужно реализовать 2 итератора.

+0

Так что это вопрос? Вы не можете перебирать свою коллекцию более одного раза? – GalacticCowboy

+0

Неясно, что вы имеете в виду. Являются ли разные субколлекции T и CoolNode или просто разные представления одной коллекции? Во-первых, вы должны сделать, например, J.13.L, и иметь методы или свойства, которые возвращают соответствующий Enumerable для каждого. Во-вторых, вы можете использовать соответствующий перевод или преобразование. –

ответ

3

Просто сделать CoolCollection<T> явно реализовать IEnumerable<CoolNode<T>>, а также IEnumerable<T>. (Я предполагаю, что это действительно CoolNode<T>, но если нет, просто возьмите лишние <T> везде.)

Это позволит вам выполнять итерацию в обоих направлениях, хотя вам понадобится бросок.

Чтобы сделать это, вам нужно что-то вроде:

class CoolCollection<T> : ICollection<T>, IEnumerable<CoolNode<T>> 
{ 
    IEnumerator<CoolNode<T>> IEnumerable<CoolNode<T>>.GetEnumerator() 
    { 
     ///...Do work here... 
    } 

    IEnumerator<T> GetEnumerator() 
    { 
     ///...Do work here... 
    } 
} 

Используя это будет выглядеть примерно так:

foreach (T item in coolCollection) 
{ 
    ... 
} 


foreach (CoolNode<T> node in (IEnumerable<CoolNode<T>>)coolCollection) 
{ 
    ... 
} 

Другим вариантом было бы выставить свойство для «узлов» , так что вы могли бы сделать:

foreach(var nodes in coolCollection.Nodes) 
{ ... } 

Чтобы реализовать это, вы немного измените ситуацию. Вам нужно будет сделать частный класс, который реализовал перечислитель ... что-то вроде:

class CoolCollection<T> : ICollection<T> 
{ 
    private List<CoolNode<T>> nodes; 

    IEnumerable<CoolNode<T>> Nodes 
    { 
     get 
     { 
      foreach(var node in this.nodes) { yield return node; } 
     } 
    } 
} 
+0

Спасибо, Рид. Зачем мне нужен актерский состав? Также я отредактировал вопрос, чтобы было ясно, если это поможет. –

+0

Я добавил пример - вам понадобится бросок, поэтому компилятор рассматривает coolCollection как IEnumerable вместо IEnumerable в этом случае. Я также предполагаю, что это CoolNode , если узлы являются частью общей коллекции ... –

+0

По крайней мере, вы должны упомянуть, что это плохая практика ... –

1

Нет, вы не можете этого сделать. Вы не можете перегружать итератор по умолчанию.

Представьте, можете ли вы перегрузить итератор по умолчанию.

Что бы это значило? foreach (object o in foo), логического выбора правильного итератора не было бы.

Что вы можете сделать, это иметь второй метод с именем ForEach2, который выполняет итерацию через вашу коллекцию по-другому. Или вы можете явно реализовать интерфейс. Или вы можете использовать композицию Linq для такого рода вещей.

С дизайнерского класса перспективы:

interface IBar { 
    IEnumerator<string> GetEnumerator(); 
} 

class Foo : IBar, IEnumerable<int> { 

    // Very bad, risky code. Enumerator implementations, should 
    // line up in your class design. 
    public IEnumerator<int> GetEnumerator() 
    { 
     yield return 1; 
     yield return 2; 
     yield return 3; 
     yield return 4; 
    } 

    IEnumerator<string> IBar.GetEnumerator() 
    { 
     yield return "hello"; 
    } 

    // must be IEnumerable if you want to support foreach 
    public IEnumerable<string> AnotherIterator 
    { 
     get { 
      yield return "hello2"; 
     } 
    } 


    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

} 

LINQ расширения для EachPair

struct Pair<T> { 
    public T First; 
    public T Second; 
} 

static class LinqExtension { 
    public static IEnumerable<Pair<T>> EachPair<T>(this IEnumerable<T> input) { 
     T first = default(T); 
     bool gotFirst = false; 
     foreach (var item in input) 
     { 
      if (!gotFirst) 
      { 
       first = item; 
       gotFirst = true; 
      } 
      else { 
       yield return new Pair<T>() { First = first, Second = item }; 
       gotFirst = false; 
      } 
     } 
    } 
} 

тестовый код:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var foo = new Foo(); 

     foreach (int number in foo) 
     { 
      Console.WriteLine(number); 
     } 

     // LINQ composition - a good trick where you want 
     // another way to iterate through the same data 
     foreach (var pair in foo.EachPair()) 
     { 
      Console.WriteLine("got pair {0} {1}", pair.First, pair.Second); 
     } 

     // This is a bad and dangerous practice. 
     // All default enumerators should be the same, otherwise 
     // people will get confused. 
     foreach (string str in (IBar)foo) 
     { 
      Console.WriteLine(str); 
     } 

     // Another possible way, which can be used to iterate through 
     // a portion of your collection eg. Dictionary.Keys 
     foreach (string str in foo.AnotherIterator) 
     { 
      Console.WriteLine(str); 
     } 
    } 
+0

Может кто-нибудь сказать мне, почему это худший ответ? –

+0

В первом блоке: IEnumerable AnotherIterator должен быть IEnumerator

+0

Также: EachPair, похоже, не имеет отношения к делу? В чем его цель? Это перечисление и построение пар (элемент 0 и 1, затем 1 и 2, 2 и 3, ...) Кажется, не соответствует другой информации .... –

2

Если я правильно понял вопрос ...

Вы могли бы сделать его похожим на некоторые из других объектов коллекции это сделать:

, например:

foreach (int key in IDictionary.Keys) 
{ 

} 

foreach (object value in IDictionary.Values) 
{ 

} 

Но я не думаю, что есть способ сделать именно так, как вы есть написано ...

0

Если CoolCollection реализует IEnumerable, вы можете написать:

foreach (var item in coolCollection) 
{ 
    ... 
} 

или если Т CoolNode

foreach (CoolNode node in coolCollection) 
{ 
    ... 
} 

Если вам нужно каким-то образом преобразовать каждый элемент к вашему типу, вы можете использовать Linq Выбор оператора:

foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item)) 
{ 
    ... 
} 
0

Посмотрите на iterindex сниппета. В вашем классе введите iterindex и нажмите [TAB]. Это поможет вам внедрить шаблон «Именованный итератор».

Результат может быть использован, как это:

foreach (var e in myTree.DepthFirstView) // supports iteration 
{ 
    if (e == myTree.DepthFirstView[2]) // supports indexing 
    { 
     // ... 
    } 
} 

(я писал этот отрывок, но я подозреваю, что он никогда не был поставлен на хорошее использование тех.).

Смежные вопросы