Можно ли написать несколько итераторов для типа в C #? - PullRequest
1 голос
/ 15 мая 2009

Так для типа, как:

CoolCollection<T>

Вы можете иметь:

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

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

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

РЕДАКТИРОВАТЬ: Извините, если это не было ясно. В основном, CoolNode - это узел, который делает CoolCollection. CoolNode имеет свойство value для возврата T, но мне нужен другой итератор для возврата только CoolNodes.

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

Ответы [ 5 ]

3 голосов
/ 15 мая 2009

Просто сделайте 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; }
        }
    }
}
2 голосов
/ 15 мая 2009

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

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

например:

foreach (int key in IDictionary.Keys)
{

}

foreach (object value in IDictionary.Values)
{

}

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

1 голос
/ 15 мая 2009

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

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

Что бы это сделать? 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 голосов
/ 15 мая 2009

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

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

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

(Я написал этот фрагмент, но подозреваю, что он никогда не использовался с пользой. Когда-либо.)

0 голосов
/ 15 мая 2009

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

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

или, если T - CoolNode

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

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

foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item))
{
    ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...