2017-08-30 45 views
7

鑑於以下接口和兩個類:簡化與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<IMyObj>(); 

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操作符相同,但我無法生成相同的輸出。

回答

15

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(); 
0

,可以串聯使用[Union][1]兩個列表:

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

Alternativly一個接一個:

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

var result = l1.Concat(l2); 

或者l1.AddRange(l2)

當然,所有這些方法都不會與您的inout具有相同的順序,因爲首先存儲所有關閉MyObj1的實例,然後存儲所有實例MyOb2

+4

請注意,與「SelectMany」不同,它不給出與OP原始代碼相同的順序。我還建議使用'Concat'而不是'Union'作爲集合行爲還沒有被指定......最後,你需要使用接口指定類型參數,否則將不能編譯。 –