简体   繁体   English

奇怪的委托参考行为

[英]Strange Delegate Reference Behaviour

I have a need to pass the results of a source function (which returns an IEnumerable ) through a list of other processing functions (that each take and return an IEnumerable ). 我有一个需要通过一个源函数的结果(它返回一个IEnumerable通过的其它处理功能的列表)(每个取并返回一个IEnumerable )。

All is fine up to that point, but I also need to allow the processing functions to perform multiple loops over their input enumerables. 到那时为止一切都很好,但是我还需要允许处理功能对其输入可枚举执行多个循环。

So rather than pass in IEnumerable<T> , I thought I would change the input parameter to Func<IEnumerable<T>> and allow each of the functions to restart the enumerable if required. 因此,与其传递IEnumerable<T> ,不如我将输入参数更改为Func<IEnumerable<T>>并允许每个函数在需要时重新启动可枚举。

Unfortunately, I'm now getting a stack overflow where the final processing function is calling itself rather than passing the request back down the chain. 不幸的是,我现在遇到了堆栈溢出,最终处理功能正在调用自身而不是将请求向下传递回链。

The example code is a bit contrived but hopefully gives you an idea of what I'm trying to achieve. 示例代码有些人为设计,但希望可以使您对我要实现的目标有所了解。

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

}

getResults is captured as a variable , not a value. getResults被捕获为变量 ,而不是值。 I don't really like the overall approach you're using here (it seems convoluted), but you should be able to fix the stackoverflow by changing the capture: 我不太喜欢您在这里使用的整体方法(看起来有些费解),但是您应该能够通过更改捕获来解决stackoverflow:

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

On a side note: IEnumerable[<T>] is already kinda repeatable, you simply call foreach another time - is is IEnumerator[<T>] that (despite the Reset() ) isn't - but also, I think it is worth doing trying to do this without needing to ever repeat the enumeration, since in the general case that simply cannot be guaranteed to work. 附带说明: IEnumerable[<T>]已经有点可重复了,您只需再次调用foreachIEnumerator[<T>]是否(尽管Reset() )不是-但我认为是无需重复进行枚举就值得尝试这样做,因为在一般情况下 ,这不能保证能正常工作。


Here's a simpler (IMO) implementation with the same result: 这是一个更简单的(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);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM