奇怪的委托参考行为

发布于 2024-10-14 19:57:31 字数 1392 浏览 7 评论 0 原文

我需要通过其他处理函数列表(每个函数接受并返回一个 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;
        }
    }

}

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).

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.

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

能否归途做我良人 2024-10-21 19:57:31

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

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

附注: IEnumerable[]< /code> 已经有点可重复,您只需再次调用 foreach - 就是 IEnumerator[] (尽管 < code>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);
        }
    }
}

getResults is captured as a variable, not a value. 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:

    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.


Here's a simpler (IMO) implementation with the same result:

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);
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文