2017-08-30 65 views
7

Với giao diện sau và hai lớp:Đơn giản hóa một vòng lặp foreach với LINQ (chọn hai đối tượng trong mỗi lần lặp)

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}"; 
} 

Và trao sau logic đó sử dụng chúng:

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

Bài kiểm tra đi và tôi thấy bên trong list chính xác những gì tôi muốn - hai trường hợp đối tượng trên một số:

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} 

Câu hỏi của tôi là, làm cách nào để đơn giản hóa logic vòng lặp foreach với LINQ? Tôi nghĩ rằng có thể có một cách thanh lịch trong việc làm tương tự với các nhà điều hành SelectMany có lẽ, nhưng tôi đã không thể sản xuất cùng một đầu ra.

Trả lời

15

SelectMany thực sự là những gì bạn cần:

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

Nói cách khác, đối với mỗi số, tạo ra một mảng hai yếu tố, sau đó được sử dụng để làm phẳng SelectMany.

Phần new IMyObj[] là bắt buộc thay vì chỉ new[] vì suy luận loại không thể cho biết loại mảng bạn muốn. Nếu các loại là như nhau, bạn có thể làm một cái gì đó như:

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

Bạn có thể ghép hai danh sách sử dụng [Union][1]:

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

Alternativly từng người một:

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

var result = l1.Concat(l2); 

Hoặc l1.AddRange(l2).

Tất nhiên, tất cả các phương pháp tiếp cận đó sẽ không có cùng thứ tự như inout của bạn vì tất cả các trường hợp trước hết là MyObj1 được lưu trữ và sau đó tất cả các trường hợp MyOb2.

+4

Lưu ý rằng điều đó không đưa ra thứ tự giống như mã ban đầu của OP, không giống như 'SelectMany'. Tôi cũng khuyên bạn nên sử dụng 'Concat' thay vì' Union' như hành vi khôn ngoan đã không được chỉ định ... và cuối cùng, bạn cần chỉ định các đối số kiểu bằng cách sử dụng giao diện như cách khác sẽ không biên dịch. –

Các vấn đề liên quan