使用LINQ简化foreach循环(在每次迭代中选择两个对象)

给定以下接口和两个类:

public interface IMyObj { int Id { get; set; } } public class MyObj1 : IMyObj { public MyObj1(int id) { Id = id; } public int Id { get; set; } public override string ToString() => $"{GetType().Name} : {Id}"; } public class MyObj2 : IMyObj { public MyObj2(int id) { Id = id; } public int Id { get; set; } public override string ToString() => $"{GetType().Name} : {Id}"; } 

并给出了以下使用它们的逻辑:

 var numbers = new[] { 1, 5, 11, 17 }; var list = new List(); foreach (var n in numbers) { // I'd like to simplify this part with LINQ... list.Add(new MyObj1(n)); list.Add(new MyObj2(n)); } Assert.AreEqual(8, list.Count); 

测试通过了,我在内部list看到了我想要的内容 – 每个数字有两个对象实例:

 Count = 8 [0]: {MyObj1 : 1} [1]: {MyObj2 : 1} [2]: {MyObj1 : 5} [3]: {MyObj2 : 5} [4]: {MyObj1 : 11} [5]: {MyObj2 : 11} [6]: {MyObj1 : 17} [7]: {MyObj2 : 17} 

我的问题是,如何使用LINQ简化foreach循环逻辑? 我想也许可以用SelectMany运算符做同样的优雅方式,但是我无法生成相同的输出。

SelectMany确实是你想要的:

 var list = numbers.SelectMany(n => new IMyObj[] { new MyObj1(n), new MyObj2(n) }) .ToList(); 

换句话说,对于每个数字,创建一个双元素数组,然后使用SelectMany将其展平。

new IMyObj[]部分是必需的,而不仅仅是new[]因为类型推断无法分辨出你想要的数组类型。 如果类型相同,您可以执行以下操作:

 var list = numbers.SelectMany(n => new[] { new MyObj(n), new MyObj(n) }) .ToList(); 

您可以使用[Union][1]连接两个列表:

 var result = numbers.Select(x => (IMyObj) new MyObj1(x)) .Union(numbers.Select(x => x => new MyObj2(x))); 

另外一个接一个:

 var l1 = numbers.Select(x => new MyObj1(x)).Cast().ToList(); var l2 = numbers.Select(x => new MyObj(x)); var result = l1.Concat(l2); 

l1.AddRange(l2)

当然,所有这些方法都不会与你的inout具有相同的顺序,因为存储了MyObj1的所有实例,然后是MyOb2所有实例。