奇怪的代表参考行为

时间:2011-01-31 09:38:55

标签: c# delegates reference ienumerable stack-overflow

我需要通过其他处理函数列表传递源函数(返回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 个答案:

答案 0 :(得分:4)

getResults被捕获为变量,而不是值。我真的不喜欢你在这里使用的整体方法(看起来很复杂),但你应该能够通过改变捕获来修复stackoverflow:

    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);
        }
    }
}