C# 奇怪的委托引用行为

C# 奇怪的委托引用行为,c#,delegates,reference,ienumerable,stack-overflow,C#,Delegates,Reference,Ienumerable,Stack Overflow,我需要将源函数的结果(返回一个IEnumerable)传递给其他处理函数的列表(每个处理函数接受并返回一个IEnumerable) 在这一点上一切都很好,但我还需要允许处理函数在其输入枚举上执行多个循环 因此,我不想传入IEnumerable,而是想将输入参数更改为Func,并允许每个函数在需要时重新启动枚举 不幸的是,我现在遇到了堆栈溢出,最终的处理函数正在调用自己,而不是将请求传递回链 示例代码有点做作,但希望能让您了解我试图实现的目标 class Program { public

我需要将源函数的结果(返回一个
IEnumerable
)传递给其他处理函数的列表(每个处理函数接受并返回一个
IEnumerable

在这一点上一切都很好,但我还需要允许处理函数在其输入枚举上执行多个循环

因此,我不想传入
IEnumerable
,而是想将输入参数更改为
Func
,并允许每个函数在需要时重新启动枚举

不幸的是,我现在遇到了堆栈溢出,最终的处理函数正在调用自己,而不是将请求传递回链

示例代码有点做作,但希望能让您了解我试图实现的目标

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

}
类程序
{
公共静态void Main(字符串[]args)
{
Func getResults=()=>GetInputValues(“A”,5);
List valuesToAppend=新列表();
附加价值。添加(“B”);
附加价值。添加(“C”);
foreach(valuesToAppend中的var项目)
{
getResults=()=>ProcessValues(()=>getResults(),项);
}
foreach(getResults()中的var项)
{
控制台写入线(项目);
}
}
公共静态IEnumerable GetInputValue(字符串值、Int32 numValue)
{
for(int i=0;i
getResults
被捕获为变量,而不是值。我真的不喜欢您在这里使用的整体方法(看起来很复杂),但您应该能够通过更改捕获来修复stackoverflow:

    foreach (var item in valuesToAppend)
    {
         var tmp1 = getResults;
         var tmp2 = item;
         getResults = () => ProcessValues(() => tmp1(),tmp2);
    }
另一方面:
IEnumerable[]
已经有点可重复了,您只需另一次调用
foreach
即可-这是
IEnumerator[]
(尽管
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);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
班级计划{
公共静态void Main(){
IEnumerable getResults=可枚举。重复(“A”,5);
List valuesToAppend=新列表{“B”,“C”};
foreach(valuesToAppend中的var项目){
字符串tmp=项目;
getResults=getResults.Select(s=>s+“”+tmp);
}
foreach(getResults中的var项){
控制台写入线(项目);
}
}
}

第一个示例避免了堆栈溢出,但现在返回:ACC、ACC等。谢谢。现在开始工作!现实世界中的处理代码需要处理内存中的大量项,因此需要多次传递才能在内存不足的情况下工作。我还没有意识到IEnumerable足够聪明,可以自己重复——有什么理由我不应该依赖这种行为吗?@rob-yes;它完全取决于枚举器的底层实现。例如,我可能会编写一个枚举器,从打开的套接字中提取数据,但不能重复。或者,如果我是邪恶的,我可以编写一个枚举器,从
随机
实例中提取数据,或者
Guid
-如果你可以重复这些,那么你做得很好。@rob-这些问题会以完全相同的方式影响基于函数的代码。因此,您增加了一层复杂性,但没有解决问题。源始终是磁盘读取(在方法内部重置位置),因此我很乐意使用更简单的选项并放弃代理。谢谢你的帮助,马克。