Странное поведение делегата - PullRequest
2 голосов
/ 31 января 2011

Мне нужно передать результаты функции-источника (которая возвращает IEnumerable) через список других функций обработки (которые каждая принимает и возвращает IEnumerable).

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

Поэтому вместо того, чтобы передавать IEnumerable<T>, я решил изменить входной параметр на Func<IEnumerable<T>> и разрешить каждой функции перезапускать перечисляемое при необходимости.

К сожалению, теперь я получаю переполнение стека, когда конечная функция обработки вызывает себя, а не передает запрос обратно по цепочке.

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

class Program
{
    public static void Main(string[] args)
    {
        Func<IEnumerable<String>> getResults = () => GetInputValues("A", 5);

        List<String> valuesToAppend = new List<String>();

        valuesToAppend.Add("B");
        valuesToAppend.Add("C");

        foreach (var item in valuesToAppend)
        {
             getResults = () => ProcessValues(() => getResults(),item);
        }

        foreach (var item in getResults())
        {
            Console.WriteLine(item);
        }

    }

    public static IEnumerable<String> GetInputValues(String value, Int32 numValues)
    {
        for (int i = 0; i < numValues; i++)
        {
            yield return value;
        }
    }

    public static IEnumerable<String> ProcessValues(Func<IEnumerable<String>> getInputValues, String appendValue)
    {
        foreach (var item in getInputValues())
        {
            yield return item + " " + appendValue;
        }
    }

}

1 Ответ

4 голосов
/ 31 января 2011

getResults фиксируется как переменная , а не значение. Мне не очень нравится общий подход, который вы здесь используете (он выглядит запутанным), но вы должны иметь возможность исправить переполнение стека, изменив перехват:

    foreach (var item in valuesToAppend)
    {
         var tmp1 = getResults;
         var tmp2 = item;
         getResults = () => ProcessValues(() => tmp1(),tmp2);
    }

С другой стороны: IEnumerable[<T>] уже вроде повторяется, вы просто звоните foreach, в другой раз - это IEnumerator[<T>], что (несмотря на Reset()) не так - но также Я думаю, что стоит попытаться сделать это без необходимости повторять перечисление, поскольку в общем случае , который просто не может быть гарантированно работать.


Вот более простая (IMO) реализация с тем же результатом:

using System;
using System.Collections.Generic;
using System.Linq;
class Program {
    public static void Main() {
        IEnumerable<String> getResults = Enumerable.Repeat("A", 5);
        List<String> valuesToAppend = new List<String> { "B", "C" };
        foreach (var item in valuesToAppend) {
            string tmp = item;
            getResults = getResults.Select(s => s + " " + tmp);
        }
        foreach (var item in getResults) {
            Console.WriteLine(item);
        }
    }
}
...