在两个列表中应用运算符的C#方法是什么?
我习惯于这样做(来自其他语言):
a = 1, 2, 3; b = 5, 1, 2; c = a * b; // c = 5, 2, 6
这需要两个相同大小的列表,并将一个函数应用于其成员,一次一个地获得结果列表。 它可能是一个像乘法(上面)那样简单的函数或更复杂的函数:
c = b>a ? ba : 0; // c = 4, 0, 0
我可以想到在C#中用几种不同的方法来做到这一点,但我不确定一个C#训练的程序员会怎么做。 在C#世界中进行这种操作的正确方法是什么?
(我所问的唯一部分是 c = f(a,b)
。我熟悉创build列表和访问元素。)
var c = a.Zip(b, (x, y) => x * y);
对于编辑之后的更复杂的一个:
var c = a.Zip(b, (x, y) => x > y ? x - y : 0);
请注意, Zip
是Enumerable
一个扩展方法,它作用于IEnumerable<T>
和Queryable
,作用于IQueryable<T>
,因此,作为数据库上的SQL查询处理,或者在.NET中内存之外的其他方式处理。
有人提到,在评论中4.0是新的。 自己实现3.5并不难:
public class MyExtraLinqyStuff { public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) { //Do null checks immediately; if(first == null) throw new ArgumentNullException("first"); if(second == null) throw new ArgumentNullException("second"); if(resultSelector == null) throw new ArgumentNullException("resultSelector"); return DoZip(first, second, resultSelector); } private static IEnumerable<TResult> DoZip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) { using(var enF = first.GetEnumerator()) using(var enS = second.GetEnumerator()) while(enF.MoveNext() && enS.MoveNext()) yield return resultSelector(enF.Current, enS.Current); } }
对于.NET2.0或.NET3.0,您可以使用相同的扩展方法,但不能从注释中回答另一个问题。 当时在.NET中并没有真正的做这种事情的习惯性方式,或者至less在我们这些用.NET编码的时候没有达成一致的共识。 我们中的一些人在我们的工具包中有类似上面的方法(尽pipe显然不是扩展方法),但是更多的是我们受到其他语言和库的影响,而不是其他任何东西(比如我正在做类似上面的事情, C ++的STL,但这不是唯一可能的灵感来源)
假设.net 3.5与等长列表:
var a = new List<int>() { 1, 2, 3 }; var b = new List<int>() { 5, 1, 2 }; var c = a.Select((x, i) => b[i] * x);
结果:
五
2
6
DotNetFiddle.Net示例
如果你不使用.NET 4.0,这里是如何编写你自己的扩展方法做一个Zip。
static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) { using (IEnumerator<TFirst> e1 = first.GetEnumerator()) using (IEnumerator<TSecond> e2 = second.GetEnumerator()) { while (e1.MoveNext() && e2.MoveNext()) { yield return resultSelector(e1.Current, e2.Current); } } }
对于没有LINQ的.NET版本,我会推荐一个for循环来完成这个工作:
List<int> list1 = new List<int>(){4,7,9}; List<int> list2 = new List<int>(){11,2,3}; List<int> newList = new List<int>(); for (int i = 0; i < list1.Count; ++i) { newList.Add(Math.Max(list1[i], list2[i])); }
当然,这个列表是相同的大小,并没有改变。 如果你事先知道列表的大小,你也可以将它实例化为正确的大小,然后在循环中设置元素。