我似乎对此有点困惑,但我在Linq的经历并不是很好。基本上,我有类似这样的代码:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
public class B
{
public List<Point> Points { get; set; }
public B(IEnumerable<int> Xs, IEnumerable<int> Ys)
{
// How to best combine Xs and Ys into Points ?
}
}
现在,我如何填写该构造函数以正确连接这些集合?我通常会使用.Join()
,但没有内键可以加入。它也应该能够处理一个数组中的元素少于另一个数组或者是空的可能性(应该永远不会发生,但它是可能的)。
答案 0 :(得分:4)
在C#4中,您可以使用Zip operator:
var result = Xs.Zip( Ys, (a,b) => new Point(a,b) );
在C#3中,您可以在其中一个序列上使用ElementAt
并选择执行相同的操作:
var result = Xs.Select( (x,i) => new Point( x, Ys.ElementAt(i) );
第二个选项的主要问题是如果ElementAt
集合本身就是一个投影(而不是实现本机索引操作的东西,如数组或List),IEnumerable
可能会非常昂贵。您可以通过首先强制第二个集合成为列表来解决这个问题:
var YsAsList = Ys.ToList();
var result = Xs.Select( (x,i) => new Point( x, YsAsList.ElementAt(i) );
您必须处理的另一个问题(如果您不使用Zip)是如何处理不平衡的集合。如果一个序列比另一个序列长,则必须确定正确的分辨率应该是多少。您的选择是:
如果您使用Zip()
,则会自动结束第二个选项,如文档所示:
该方法将第一个序列的每个元素与一个元素合并 在第二个中具有相同的索引 序列。如果序列没有 相同数量的元素, 方法合并序列直到它 到达其中一个的末尾。对于 例如,如果一个序列有三个 元素和另一个有四个, 结果序列只有 三要素。
所有这一切的最后一步是,您需要将项目结果转换为列表,以将其分配给Points
对象。这部分很简单,使用ToList()
方法:
Points = Xs.Select( (x,i) => new Point( x, Ys.ElementAt(i) ).ToList();
或在C#4中:
Points = Xs.Zip( Ys, (a,b) => new Point(a,b) ).ToList();
答案 1 :(得分:1)
如果两个集合的大小不同或者其中一个或两个都为空,那么大概应该是一个错误。
public B(IEnumerable<int> Xs, IEnumerable<int> Ys)
{
if (Xs == null || Ys == null)
throw new ArgumentNullException( "Both collections need to be non-null" );
if (Xs.Count() != Ys.Count())
throw new ArgumentException( "Collections must be of the same size" );
this.Points = Xs.Zip( (x,y) => new Point { X = x, Y = y } ).ToList();
}
如果您需要.NET 3.5的内容,请将最后一行替换为。
this.Points = Xs.Select( (x,i) => new Point { X = x, Y = Ys.ElementAt(i) } ).ToList();