2009-08-26 49 views
12

Xem xét yêu cầu để thay đổi thành viên dữ liệu trên một hoặc nhiều thuộc tính của đối tượng có độ sâu 5 hoặc 6 cấp.Thay thế foreach lồng nhau bằng LINQ; sửa đổi và cập nhật thuộc tính sâu bên trong

Có các bộ sưu tập phụ cần được lặp lại để truy cập vào thuộc tính cần kiểm tra sửa đổi &.

Ở đây chúng tôi đang gọi một phương pháp dọn sạch địa chỉ đường phố của một Nhân viên. Vì chúng ta đang thay đổi dữ liệu trong các vòng, việc thực hiện hiện nay cần một vòng lặp for để ngăn chặn sự ngoại lệ:

Không thể gán cho "someVariable" vì nó là một 'foreach lặp biến'

Dưới đây là thuật toán hiện tại (obfuscated) với lồng nhau foreachfor.

foreach (var emp in company.internalData.Emps) 
{ 
    foreach (var addr in emp.privateData.Addresses) 
    { 
     int numberAddresses = addr.Items.Length; 

     for (int i = 0; i < numberAddresses; i++) 
     { 
      //transform this street address via a static method 
      if (addr.Items[i].Type =="StreetAddress") 
       addr.Items[i].Text = CleanStreetAddressLine(addr.Items[i].Text); 
     } 
    } 
} 

Câu hỏi: thuật toán này có thể được thực hiện lại sử dụng LINQ? Yêu cầu là cho bộ sưu tập gốc để dữ liệu của nó được thay đổi bằng cuộc gọi phương thức tĩnh đó.

Cập nhật: Tôi đã suy nghĩ/nghiêng về hướng của giải pháp loại jQuery/chọn. Tôi không nói cụ thể câu hỏi này theo cách đó. Tôi nhận ra rằng tôi đã vượt qua ý tưởng đó (không có tác dụng phụ). Cảm ơn tất cả mọi người! Nếu có một cách để thực hiện một bộ chọn giống jQuery, hãy xem nó!

Trả lời

11

LINQ không có ý định thay đổi bộ các đối tượng. Bạn sẽ không mong đợi một câu lệnh SELECT sql để sửa đổi các giá trị của các hàng được chọn, đúng không? Nó giúp để nhớ những gì LINQ là viết tắt - Ngôn ngữ tích hợp tự nhiên Truy vấn. Sửa đổi các đối tượng trong một truy vấn LINQ là, IMHO, một mẫu chống.

Câu trả lời của Stan R. sẽ là giải pháp tốt hơn khi sử dụng vòng lặp foreach, tôi nghĩ vậy.

+0

cảm ơn. của bạn và câu trả lời của Merhdad giống hệt nhau, nhưng tôi đã thấy bạn đầu tiên :) Tôi nhận thấy nó sẽ là vô nghĩa để gõ lại mã đó, nhưng tôi cảm thấy điều quan trọng là làm cho vấn đề phụ về hiệu ứng phụ –

+0

LINQ không phải là máy chủ SQL, mặc dù việc thực hiện cho SQL khác với điều đó với .NET - Bạn vẫn có thể thay đổi các thuộc tính với cả hai bộ (LINQ to SQL) và chỉ C# LINQ. – ppumkin

18
foreach(var item in company.internalData.Emps 
         .SelectMany(emp => emp.privateData.Addresses) 
         .SelectMany(addr => addr.Items) 
         .Where(addr => addr.Type == "StreetAddress")) 
    item.Text = CleanStreetAddressLine(item.Text); 
+0

sẽ SelectMany cải thiện hiệu suất qua forested foreach? – ManirajSS

+0

Hiệu suất LINQ không đáng kể đối với câu trả lời này đẹp đến mức nào. (nhưng không ... LINQ là khá damn nhanh) – ppumkin

1

LINQ không cung cấp tùy chọn có phản ứng phụ. Tuy nhiên bạn có thể làm:

company.internalData.Emps.SelectMany(emp => emp.Addresses).SelectMany(addr => Addr.Items).ToList().ForEach(/*either make an anonymous method or refactor your side effect code out to a method on its own*/); 
12
var dirtyAddresses = company.internalData.Emps.SelectMany(x => x.privateData.Addresses) 
               .SelectMany(y => y.Items) 
               .Where(z => z.Type == "StreetAddress"); 

    foreach(var addr in dirtyAddresses) 
    addr.Text = CleanStreetAddressLine(addr.Text); 
0

Bạn có thể thực hiện việc này nhưng không thực sự muốn. Một số blogger đã nói về tính chất chức năng của LINQ, và nếu bạn nhìn vào tất cả các phương pháp được cung cấp bởi Linq, bạn sẽ thấy rằng chúng không tạo ra các tác dụng phụ. Chúng tạo ra các giá trị trả về, nhưng chúng không thay đổi bất cứ thứ gì khác. Tìm kiếm các đối số trên một phương thức Linq ForEach, và bạn sẽ nhận được một lời giải thích tốt về khái niệm này.

Với ý nghĩ đó, những gì bạn probaly muốn là một cái gì đó như thế này:

var addressItems = company.internalData.Emps.SelectMany(
    emp => emp.privateData.Addresses.SelectMany(
      addr => addr.Items 
    ) 
); 
foreach (var item in addressItems) 
{ 
    ... 
} 

Tuy nhiên, nếu bạn muốn thực hiện chính xác những gì bạn hỏi, thì đây là hướng mà bạn sẽ cần phải đi:

var addressItems = company.internalData.Emps.SelectMany(
    emp => emp.privateData.Addresses.SelectMany(
      addr => addr.Items.Select(item => 
      { 
       // Do the stuff 
       return item; 
      }) 
    ) 
); 
10

I don't like mixing "query comprehension" syntax and dotted-method-call syntax trong cùng một tuyên bố.

Tôi thích ý tưởng tách truy vấn khỏi hành động . Đây là những khác biệt ngữ nghĩa, do đó, tách chúng trong mã thường có ý nghĩa.

var addrItemQuery = from emp in company.internalData.Emps 
        from addr in emp.privateData.Addresses 
        from addrItem in addr.Items 
        where addrItem.Type == "StreetAddress" 
        select addrItem; 

foreach (var addrItem in addrItemQuery) 
{ 
    addrItem.Text = CleanStreetAddressLine(addrItem.Text); 
} 

Một vài lưu ý về mã của bạn; đây là những cá nhân, vì vậy tôi bạn có thể không đồng ý:

  • Nói chung, tôi tránh chữ viết tắt (Emps, emp, addr)
  • tên không nhất quán là khó hiểu hơn (addr vs Addresses): chọn một và gắn bó với số điện thoại
  • Từ "số" là phi thường. Nó có thể là một danh tính ("tù nhân số 378 xin vui lòng bước về phía trước.") Hoặc một số ("số lượng cừu trong lĩnh vực đó là 12."). Vì chúng ta sử dụng cả hai khái niệm trong mã rất nhiều, nó rất có giá trị để có được điều này rõ ràng. Tôi thường sử dụng "index" cho cái đầu tiên và "count" cho cái thứ hai.
  • Có trường type là một chuỗi là một mã nguồn. Nếu bạn có thể đặt mã này là enum, mã của bạn có thể sẽ tốt hơn.
+0

Cảm ơn bạn đã bình luận Jay. Mã không được sao chép từ dự án thực tế, và bị làm phiền cho tiêu dùng công cộng. –

+0

Tôi thứ hai này. Việc tách truy vấn khỏi bản cập nhật giúp giải pháp sạch hơn và dễ bảo trì hơn. Điều này tốt hơn nhiều so với các giải pháp biểu thức lambda "tất cả trong một" khác được liệt kê ở đây. – SeeMoreGain

2

Bẩn một lớp lót.

company.internalData.Emps.SelectMany(x => x.privateData.Addresses) 
    .SelectMany(x => x.Items) 
    .Where(x => x.Type == "StreetAddress") 
    .Select(x => { x.Text = CleanStreetAddressLine(x.Text); return x; }); 
0

Để cập nhật kết quả LINQ sử dụng vòng lặp foreach, đầu tiên tôi tạo ‘danh sách’ địa phương biến và sau đó thực hiện cập nhật bằng foreach Loop. Giá trị được cập nhật theo cách này. Đọc thêm tại đây:

How to update value of LINQ results using FOREACH loop

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