I'm looking for a creative pattern to enumerate two IEnumerable<>'s synchronized.
我正在寻找一个创意模式来枚举两个IEnumerable <>的同步。
If I was making something up and adding to the C# syntax I might write:
如果我正在制作一些内容并添加到C#语法中,我可能会写:
foreach(var firstItem, var secondItem in this.ListOne, this.ListTwo)
{
if (firstItem.Prop == secondItem.Prop)
WorkSomeMagic(secondItem);
DoSomethingElse(firstItem);
}
Now, obviously that doesn't exist. What patterns have people used to accomplish something similar when dealing with enumerations that aren't accessible by index? Keep in mind, what is inside my pseudo-foreach would be more complex; I simplified for the example.
现在,显然这不存在。在处理索引无法访问的枚举时,人们用什么模式来完成类似的事情?请记住,我的伪foreach中的内容会更复杂;我简化了这个例子。
5 个解决方案
#1
You're looking for Zip, which is new in .NET 4 or you can use the implementation here:
您正在寻找Zip,这是.NET 4中的新功能,或者您可以在此处使用实现:
Is there a zip-like method in .Net?
.Net中有类似拉链的方法吗?
#2
I usually do the following:
我通常会做以下事情:
using (IEnumerator<int> e1 = this.ListOne.GetEnumerator(),
e2 = this.ListTwo.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
...
}
}
Or write an extension method:
或者写一个扩展方法:
public static void EnumerateWith<T>(this IEnumerable<T> left,
IEnumerable<T> right, Action<T,T> del) {
using (IEnumerator<T> l = left.GetEnumerator(),
r = right.GetEnumerator()) {
while (l.MoveNext() && r.MoveNext()) {
del(l.Current,r.Current);
}
}
}
ListOne.EnumerateWith(ListTwo, (left, right) => {
...
});
#3
As with any general C# question, this will probably have 10 good answers posted before VS2008 even loads. Instead of that rat race, I'll come up with an offbeat "anti-pattern" you should never use. In fact, anyone writing mission critical code, please stop reading now.
与任何一般的C#问题一样,在VS2008甚至加载之前,这可能会有10个好的答案。而不是那场比赛,我会想出一个你不应该使用的另类“反模式”。事实上,任何人都在编写关键任务代码,请立即停止阅读。
using System;
using System.Collections.Generic;
using System.Linq;
class EnumTwoLists
{
static void Main(string[] args)
{
var left = new List<int>();
var right = new List<DateTime>();
var demo = new LinqAbuse<int, DateTime>(left, right);
demo.Populate(40, s => s * s, d => new DateTime(2009, d / 31 + 1, d % 31 + 1));
demo.Enumerate( (s, d) => Console.WriteLine(String.Format("Executing arbitrary code with {0} and {1}", s, d)) );
}
}
class LinqAbuse<T1, T2>
{
public LinqAbuse(List<T1> l, List<T2> r)
{
left = l;
right = r;
}
List<T1> left;
List<T2> right;
public void Populate(int size, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator)
{
new int[size].Aggregate(0, (index, empty) => PopulateWrapper(left, right, leftGenerator, rightGenerator, index));
}
int PopulateWrapper(List<T1> left, List<T2> right, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator, int index)
{
left.Add(leftGenerator(index));
right.Add(rightGenerator(index));
return ++index;
}
public void Enumerate(Action<T1, T2> loopBody)
{
left.Join(right, l => "", r => "",
(l, r) => ActionWrapper(l, r, loopBody),
new CartesianComparer<object>(right.Count))
.ToList();
}
object ActionWrapper(T1 x, T2 y, Action<T1, T2> action)
{
action(x, y);
return null;
}
}
class CartesianComparer<T> : IEqualityComparer<T>
{
public CartesianComparer(int _size)
{
size = _size;
equalsCounter = (size * (size - 1) >> 1) + size; // Combinations(size, 2) + (size - trueCounter)
}
private int size;
private int equalsCounter;
private int trueCounter = 0;
public bool Equals(T x, T y)
{
if (0 < --equalsCounter)
return false;
equalsCounter = size - ++trueCounter;
return true;
}
public int GetHashCode(T obj)
{
return 0;
}
}
Aww, isn't she cute? (alternate caption: Mommy, why is Anders crying?)
哇,她不可爱吗? (另一个标题:妈妈,安德斯为什么哭?)
#4
Ignoring checks for nulls and whatnot:
忽略检查空值和诸如此类的东西:
IEnumerable<T1> first;
IEnumerable<T2> second;
using (IEnumerator<T1> e1 = first.GetEnumerator()) {
using (IEnumerator<T2> e2 = second.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
// do something eith e1.Current and e2.Current
}
}
}
#5
I know this question is old but for anyone coming to this question now you can build on Jason's answer and JaredPar's answer with C#7's ValueTuple
, which will give you a syntax similar to the original question. You may need to install the nuget package System.ValueTuple
.
我知道这个问题已经过时了,但是对于任何想出这个问题的人来说,现在你可以利用Jason的答案和JaredPar对C#7的ValueTuple的回答,它会给你一个类似于原始问题的语法。您可能需要安装nuget包System.ValueTuple。
If you declare an extension method something like this:
如果你声明一个像这样的扩展方法:
internal static class EnumerableExtensions
{
internal static IEnumerable<(T1, T2)> EnumerateWith<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while(firstEnumerator.MoveNext() && secondEnumerator.MoveNext())
{
yield return (firstEnumerator.Current, secondEnumerator.Current);
}
}
}
}
Then you can use it like this:
然后你可以像这样使用它:
List<Foo> foos = new List<Foo>()
{
new Foo(),
new Foo(),
new Foo()
};
List<Bar> bars = new List<Bar>()
{
new Bar(),
new Bar(),
new Bar()
};
foreach((Foo foo, Bar bar) in foos.EnumerateWith(bars))
{
Console.WriteLine(foo.ID);
Console.WriteLine(bar.ID);
}
#1
You're looking for Zip, which is new in .NET 4 or you can use the implementation here:
您正在寻找Zip,这是.NET 4中的新功能,或者您可以在此处使用实现:
Is there a zip-like method in .Net?
.Net中有类似拉链的方法吗?
#2
I usually do the following:
我通常会做以下事情:
using (IEnumerator<int> e1 = this.ListOne.GetEnumerator(),
e2 = this.ListTwo.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
...
}
}
Or write an extension method:
或者写一个扩展方法:
public static void EnumerateWith<T>(this IEnumerable<T> left,
IEnumerable<T> right, Action<T,T> del) {
using (IEnumerator<T> l = left.GetEnumerator(),
r = right.GetEnumerator()) {
while (l.MoveNext() && r.MoveNext()) {
del(l.Current,r.Current);
}
}
}
ListOne.EnumerateWith(ListTwo, (left, right) => {
...
});
#3
As with any general C# question, this will probably have 10 good answers posted before VS2008 even loads. Instead of that rat race, I'll come up with an offbeat "anti-pattern" you should never use. In fact, anyone writing mission critical code, please stop reading now.
与任何一般的C#问题一样,在VS2008甚至加载之前,这可能会有10个好的答案。而不是那场比赛,我会想出一个你不应该使用的另类“反模式”。事实上,任何人都在编写关键任务代码,请立即停止阅读。
using System;
using System.Collections.Generic;
using System.Linq;
class EnumTwoLists
{
static void Main(string[] args)
{
var left = new List<int>();
var right = new List<DateTime>();
var demo = new LinqAbuse<int, DateTime>(left, right);
demo.Populate(40, s => s * s, d => new DateTime(2009, d / 31 + 1, d % 31 + 1));
demo.Enumerate( (s, d) => Console.WriteLine(String.Format("Executing arbitrary code with {0} and {1}", s, d)) );
}
}
class LinqAbuse<T1, T2>
{
public LinqAbuse(List<T1> l, List<T2> r)
{
left = l;
right = r;
}
List<T1> left;
List<T2> right;
public void Populate(int size, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator)
{
new int[size].Aggregate(0, (index, empty) => PopulateWrapper(left, right, leftGenerator, rightGenerator, index));
}
int PopulateWrapper(List<T1> left, List<T2> right, Func<int, T1> leftGenerator, Func<int, T2> rightGenerator, int index)
{
left.Add(leftGenerator(index));
right.Add(rightGenerator(index));
return ++index;
}
public void Enumerate(Action<T1, T2> loopBody)
{
left.Join(right, l => "", r => "",
(l, r) => ActionWrapper(l, r, loopBody),
new CartesianComparer<object>(right.Count))
.ToList();
}
object ActionWrapper(T1 x, T2 y, Action<T1, T2> action)
{
action(x, y);
return null;
}
}
class CartesianComparer<T> : IEqualityComparer<T>
{
public CartesianComparer(int _size)
{
size = _size;
equalsCounter = (size * (size - 1) >> 1) + size; // Combinations(size, 2) + (size - trueCounter)
}
private int size;
private int equalsCounter;
private int trueCounter = 0;
public bool Equals(T x, T y)
{
if (0 < --equalsCounter)
return false;
equalsCounter = size - ++trueCounter;
return true;
}
public int GetHashCode(T obj)
{
return 0;
}
}
Aww, isn't she cute? (alternate caption: Mommy, why is Anders crying?)
哇,她不可爱吗? (另一个标题:妈妈,安德斯为什么哭?)
#4
Ignoring checks for nulls and whatnot:
忽略检查空值和诸如此类的东西:
IEnumerable<T1> first;
IEnumerable<T2> second;
using (IEnumerator<T1> e1 = first.GetEnumerator()) {
using (IEnumerator<T2> e2 = second.GetEnumerator()) {
while (e1.MoveNext() && e2.MoveNext()) {
// do something eith e1.Current and e2.Current
}
}
}
#5
I know this question is old but for anyone coming to this question now you can build on Jason's answer and JaredPar's answer with C#7's ValueTuple
, which will give you a syntax similar to the original question. You may need to install the nuget package System.ValueTuple
.
我知道这个问题已经过时了,但是对于任何想出这个问题的人来说,现在你可以利用Jason的答案和JaredPar对C#7的ValueTuple的回答,它会给你一个类似于原始问题的语法。您可能需要安装nuget包System.ValueTuple。
If you declare an extension method something like this:
如果你声明一个像这样的扩展方法:
internal static class EnumerableExtensions
{
internal static IEnumerable<(T1, T2)> EnumerateWith<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second)
{
using (var firstEnumerator = first.GetEnumerator())
using (var secondEnumerator = second.GetEnumerator())
{
while(firstEnumerator.MoveNext() && secondEnumerator.MoveNext())
{
yield return (firstEnumerator.Current, secondEnumerator.Current);
}
}
}
}
Then you can use it like this:
然后你可以像这样使用它:
List<Foo> foos = new List<Foo>()
{
new Foo(),
new Foo(),
new Foo()
};
List<Bar> bars = new List<Bar>()
{
new Bar(),
new Bar(),
new Bar()
};
foreach((Foo foo, Bar bar) in foos.EnumerateWith(bars))
{
Console.WriteLine(foo.ID);
Console.WriteLine(bar.ID);
}