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
).
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.WriteLi开发者_如何学Cne(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. 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);
}
}
}
精彩评论