2010-01-29 10 views
2

我看到的SelectMany的所有例子都是扁平数组的数组等等。我对这个问题有不同的看法。如何通过Linq将一个类型(而不是一个枚举)展平?

我有一个类型的数组,我想将该类型的内容提取到一个流中。这里是我的示例代码:

public class MyClass 
{ 
    class Foo 
    { 
     public int X, Y; 
    } 

    static IEnumerable<int> Flatten(Foo foo) 
    { 
     yield return foo.X; 
     yield return foo.Y; 
    } 

    public static void RunSnippet() 
    { 
     var foos = new List<Foo>() 
      { 
       new Foo() { X = 1, Y = 2 }, 
       new Foo() { X = 2, Y = 4 }, 
       new Foo() { X = 3, Y = 6 }, 
      }; 

     var query = foos.SelectMany(x => Flatten(x)); 
     foreach (var x in query) 
     { 
      Console.WriteLine(x); 
     } 
    } 
} 

这个输出,我想什么:1,2,2,4,3,6,

我可以消除收益率?我知道支持这件事的管道是不平凡的,而且可能有很大的成本。在linq中可以做到这一切吗?

我觉得我非常接近答案,只是缺少关键字来搜索。 :)

UPDATE:

正如下面的答复中提到,它的工作原理使用是这样的:

foos.SelectMany(x => new[] { x.X, x.Y }); 

不过,我希望能找到一个方法来做到这一点,而不会产生N/2个临时阵列。我正在针对大型选择集运行此操作。

+1

这有什么错产量的解决方案? – 2010-01-29 23:46:32

+0

我读过一篇文章,谈论如何在下面的收益率,它不便宜。我还想在工作的完成位置旁边放置一些内容,而不是要求完全独立的功能。 Lambdas ftw。 – scobi 2010-02-02 15:50:22

+0

“不便宜”从哪里来?你有链接到文章? – 2010-02-03 10:00:02

回答

1

好吧,如果你想避免临时数组创造,但你想使用LINQ短和漂亮的代码,你可以去用 -

var query = foos.Aggregate(
    new List<int>(), 
    (acc, x) => { acc.Add(x.X); acc.Add(x.Y); return acc; } 
    ); 
+0

是的!这是我一直在寻找的东西。 – scobi 2010-02-02 15:55:40

1

你可以这样做:

var query = foos.SelectMany(x => new[] { x.X, x.Y }); 
+0

糟糕 - 我忘了提及我不想这样做。试图避免临时阵列的创建。将更新我的问题。 – scobi 2010-01-29 22:56:39

0

这种挫折IEnumerable<T>的,更比得上我们与PushLINQ做 - 但它是一个很大比在飞行实施迭代器块简单(通过IL),同时通过动态方法保持致盲性能;使用object是如果你的数据是非正交的,你通过相同的API需要多种类型:

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

// the type we want to iterate efficiently without hard code 
class Foo 
{ 
    public int X, Y; 
} 
// what we want to do with each item of data 
class DemoPusher : IPusher<int> 
{ 
    public void Push(int value) 
    { 
     Console.WriteLine(value); 
    } 
} 
// interface for the above implementation 
interface IPusher<T> 
{ 
    void Push(T value); 
} 
static class Program 
{ 
    // see it working 
    static void Main() 
    { 
     Foo foo = new Foo { X = 1, Y = 2 }; 
     var target = new DemoPusher(); 
     var pushMethod = CreatePusher<int>(typeof(Foo)); 
     pushMethod(foo, target);  
    } 
    // here be dragons 
    static Action<object, IPusher<T>> CreatePusher<T>(Type source) 
    { 
     DynamicMethod method = new DynamicMethod("pusher", 
      typeof(void), new[] { typeof(object), typeof(IPusher<T>) }, source); 
     var il = method.GetILGenerator(); 
     var loc = il.DeclareLocal(source); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Castclass, source); 
     il.Emit(OpCodes.Stloc, loc); 
     MethodInfo push = typeof(IPusher<T>).GetMethod("Push"); 
     foreach (var field in source.GetFields(BindingFlags.Instance 
      | BindingFlags.Public | BindingFlags.NonPublic)) 
     { 
      if (field.FieldType != typeof(T)) continue; 
      il.Emit(OpCodes.Ldarg_1); 
      il.Emit(OpCodes.Ldloc, loc); 
      il.Emit(OpCodes.Ldfld, field); 
      il.EmitCall(OpCodes.Callvirt, push, null); 
     } 
     il.Emit(OpCodes.Ret); 
     return (Action<object, IPusher<T>>) 
      method.CreateDelegate(typeof(Action<object, IPusher<T>>)); 
    } 

} 
2

如果你担心编译器弄虚作假涉及yield和/或成本费用的SelectMany,你可以尝试通过不调用Flatten每个Foo而是Flatten,以尽量减少这些影响的foos直接:

public class MyClass 
{ 
    class Foo 
    { 
     public int X, Y; 
    } 

    static IEnumerable<int> Flatten(IEnumerable<Foo> foos) 
    { 
     foreach (var foo in foos) 
     { 
      yield return foo.X; 
      yield return foo.Y; 
     } 
    } 

    public static void RunSnippet() 
    { 
     var foos = new List<Foo>() 
     { 
      new Foo() { X = 1, Y = 2 }, 
      new Foo() { X = 2, Y = 4 }, 
      new Foo() { X = 3, Y = 6 }, 
     }; 

     var query = Flatten(foos); 

     foreach (var x in query) 
     { 
      Console.WriteLine(x); 
     } 
    } 
} 

我碰到这个小测试应用程序,我已经看到有一些pe第二次实施带来了性能优势。在我的机器上,使用两种算法压扁100,000 Foo分别花费36ms和13ms。一如既往YMMV。

相关问题