Yield Return Many?

后端 未结 6 674
粉色の甜心
粉色の甜心 2020-12-03 04:08

I find myself foreach-ing over IEnumerables a lot just so that I can return each result. Is there a way to compress something like this

foreach (var subSelec         


        
相关标签:
6条回答
  • 2020-12-03 04:37

    You can break your method into two. Given these extension methods:

    public static class MultiEnumerableExtensions {
      public static IEnumerable<T> Pack<T>(this T item) {
        yield return item;
      }
      public static IEnumerable<T> Flatten<T>(
        this IEnumerable<IEnumerable<T>> multiList) {
        return multiList.SelectMany(x => x);
      }
    }
    

    And using Eric Lippert's example, it becomes this:

    public static class BinaryTreeExtensions {
      public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root) {
        return PreorderTraversalMulti(root).Flatten();
      }
      private static IEnumerable<IEnumerable<T>> PreorderTraversalMulti<T>(
        this BinaryTree<T> root) {
        if (root == null) yield break;
        yield return root.Item.Pack(); // this packs an item into an enumerable
        yield return root.Left.PreorderTraversal();
        yield return root.Right.PreorderTraversal();
      }
    }
    

    The inner method yields enumerables of T instead of Ts, and the outer method just needs to flatten this result.

    0 讨论(0)
  • 2020-12-03 04:41

    No, there isn't, unless you completely replace every yield return with a single return statement using LINQ.

    For example:

    return someSet
        .Concat(someOtherSet.SelectMany(s => FindSingle(context, s));
    
    0 讨论(0)
  • 2020-12-03 04:45

    Use Enumerable.SelectMany:

    return subSelectors.SelectMany(subselector => FindSingle(context, subSelector));
    

    This only works if you don't have any other yield return statements in your method.

    0 讨论(0)
  • 2020-12-03 04:46

    With C# 7.0, local functions are allowed, which enables us to have a fairly neat approach

    IEnumerable<T> FlatEnumerable(){
        IEnumerable<IEnumerable<T>> NestedEnumerable(){
           yield return myEnumerable1;
           yield return myEnumerable2;
        }
    
        return NestedEnumerable().SelectMany(e => e);
    }
    

    https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/local-functions

    0 讨论(0)
  • 2020-12-03 04:54

    Use the power of Linq!

    return subSelectors.SelectMany(s => FindSingle(context, s));
    
    0 讨论(0)
  • 2020-12-03 04:57

    This is a somewhat frequently requested feature that C# does not support. See this Connect item for details:

    http://connect.microsoft.com/VisualStudio/feedback/details/256934/yield-return-to-also-yield-collections

    The proposed syntax is usually something like:

    public static IEnumerable<T> PreorderTraversal<T>(this BinaryTree<T> root)
    {
        if (root == null) yield break;
        yield return root.Item;
        yield foreach root.Left.PreorderTraversal();
        yield foreach root.Right.PreorderTraversal();
    }
    

    If you are interested in playing with a C#-like language that supports this feature, take a look at Cω:

    http://research.microsoft.com/en-us/um/cambridge/projects/comega/

    You might also want to read this paper on the feature by the implementors of Cω:

    http://research.microsoft.com/en-us/projects/specsharp/iterators.pdf

    If you're interested in a non-C#-like language that supports this feature, take a look at the "yield!" feature of F#. (I just love that the name of the feature is "yield!")

    Even if you are not interested in the theoretical stuff, it sounds like you face this situation as a practical problem. You should also read Wes Dyer's article on techniques for efficiently doing this sort of nested iteration without "yield foreach":

    http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx

    0 讨论(0)
提交回复
热议问题