IEnumerable and Recursion using yield return
Inside a method that returns IEnumerable<T>
, yield return
has to return T
, not an IEnumerable<T>
.
Replace
yield return c.GetDeepControlsByType<T>();
with:
foreach (var x in c.GetDeepControlsByType<T>())
{
yield return x;
}
Yield return in local recursive function
Nested methods don't behave any differently to regular methods with respect to the effect of yield return
.
It's not that the methods are ignored - it's that you're not using the value returned from them, which makes them pointless. In fact, you won't even enter the body of the method recursively, because that doesn't happen until you call MoveNext()
on the enumerator returned by the GetEnumerator()
call to the returned IEnumerable<>
.
Unfortunately C# doesn't have a sort of "yield foreach" syntax, so you have to iterate over all the values and yield them directly:
public IEnumerable<ElementType> GetSubtreeFlattenedPostOrder()
{
return PostOrderRecursive(this);
IEnumerable<ElementType> PostOrderRecursive(BinaryTree<ElementType> currentNode)
{
if (currentNode.HasLeft)
{
foreach (var node in PostOrderRecursive(currentNode.Left))
{
yield return node;
}
}
if (currentNode.HasRight)
{
foreach (var node in PostOrderRecursive(currentNode.Right))
{
yield return node;
}
}
yield return currentNode.element;
}
}
One point to note: this implementation performs poorly, because it needs to create so many iterators. An implementation which avoided recursion but kept a stack or queue of nodes to process could be a lot more efficient. You should bear that in mind if you have any large trees, but it may make sense to keep it simple if performance isn't an issue.
Recursion and Yield return
I can think of at least two alternatives which would be better than what you're doing now:
- Build a single
List<T>
object recursively, by passing the object to the recursive method. - Don't build a
List<T>
object at all; instead, just recursively evaluate iterators.
Example of #1 (I did not bother to try to clean up any but the most obvious bugs/syntax errors in the original code):
private IEnumerable<object> HandleChildItems()
{
var itemsList = new List<Item>();
GetChildItems(xmlnode, itemsList);
// IEnumerable<T> is covariant
return itemsList;
}
private void GetChildItems(XMLnodeList nodeList, List<Item> itemsList)
{
// Here I recursively call the method to add the items to list
foreach (xmlnode xn in nodeList)
{
if (xn.childnodes.count > 0)
{
GetChildItems(xn.childnodes, itemsList);
}
else
{
itemsList.Add(new Item { Code = "123", Itemtext = "xyz" });
}
}
}
Example of #2:
private IEnumerable<object> HandleChildItems()
{
foreach (Item item in GetChildItems(xmlnode))
{
yield return item;
}
}
private IEnumerable<Item> GetChildItems(XMLnodeList nodeList)
{
// Here I recursively call the method to add the items to list
foreach (xmlnode xn in nodeList)
{
if (xn.childnodes.count > 0)
{
foreach (Item item in GetChildItems(xn.childnodes))
{
yield return item;
}
}
else
{
yield return new Item { Code = "123", Itemtext = "xyz" };
}
}
}
Example #1 has the advantage that you create just one intermediate object, the List<Item>
itself. But it does require that the entire list be stored in memory at once.
Example #2 creates a potentially large number of iterator method objects, one for each level of recursion. But this number is likely much smaller than the total number of Item
objects you would have to create, and you do avoid having to store all those Item
objects in memory all at once.
C# how to use yield operator during recursive call?
You need to iterate your recursive call also. In your code, you only return the base case while the rest of the executions don't return anything.
private static IEnumerable<int[]> Combinations(int[] input, int len, int startPosition, int[] result)
{
if (len == 0)
{
yield return result;
}
else
{
for (int i = startPosition; i <= input.Length - len; i++)
{
result[result.Length - len] = input[i];
//// You need to return the results of your recursive call
foreach (var combination in Combinations(input, len - 1, i + 1, result))
{
yield return combination;
}
}
}
}
C# recursive yield return not returning anything
Option 1, yield each value from the recursive call.
foreach (string s in baseChars)
foreach (var r in CharsRange2(prefix + s, pos - 1))
yield return r;
Option 2, reuse existing IEnumerable
types built into the framework to avoid yield return completely;
if (pos == 1)
return baseChars.Select(s => prefix + s);
else
return baseChars.SelectMany(s => CharsRange2(prefix + s, pos - 1));
Option 3, use nested loops instead of a recursive method, left as an exercise for the reader.
Does yield return have any uses other than for IEnumerable?
Your question
Does C#'s yield return have a use outside of IEnumerables
The answer is no, you can see this by the yield
documentation
yield (C# Reference)
When you use the yield contextual keyword in a statement, you indicate
that the method, operator, or get accessor in which it appears is an
iterator. Using yield to define an iterator removes the need for an
explicit extra class (the class that holds the state for an
enumeration, see IEnumerator for an example) when you implement the
IEnumerable and IEnumerator pattern for a custom collection type.Iterator methods and get accessors
The declaration of an iterator must meet the following requirements:
>>>The return type must be IEnumerable, IEnumerable, IEnumerator, or IEnumerator <<<
IEnumerable yield and recursion - doesn't work
You need to iterate though the enumerable returned by the recursive call and yield return
each of the items explicitly.
private IEnumerable<int> fibRec(int a, int b)
{
int tmp = a;
a = b;
b = tmp + b;
yield return a;
foreach(int val in fibRec(a, b))
{
yield return val;
}
}
Recursion with yield return elements order in tree
Have you tried something like:
private IEnumerable<Node> getAllNodesRecursively(Node subnode)
{
// Return the parent before its children
yield return subnode;
foreach (Node node in subnode.Nodes)
{
foreach(Node n in getAllNodesRecursively(node))
{
yield return n;
}
}
}
Your implementation is calling getAllNodesRecursively
recursively, but ignoring its return value.
Related Topics
What Is More Efficient: Dictionary Trygetvalue or Containskey+Item
No Concurrentlist<T> in .Net 4.0
How to Check If a String Contains Any of Some Strings
Find a Wpf Element Inside Datatemplate in the Code-Behind
Getting the Application's Directory from a Wpf Application
Getting the Current Tab's Url from Google Chrome Using C#
Encrypt Connection String in App.Config
Get All Inherited Classes of an Abstract Class
How to Decode a Base64 Encoded String
How to Pass Values Across the Pages in ASP.NET Without Using Session
Using Multiple Versions of the Same Dll
Tuples( or Arrays ) as Dictionary Keys in C#
Difference Between Convert.Tostring() and .Tostring()
Adjusting Httpwebrequest Connection Timeout in C#
What Characters Are Allowed in C# Class Name