2013-05-24 33 views
5

Ok đây là một chút khó khăn để yêu cầu, nhưng tôi sẽ cố gắng. Tôi có một Danh sách với các đối tượng (Rất nhiều), trong đó lại chứa một danh sách với các đối tượng (các tấm). Khi tôi thay đổi một giá trị trong một wafer, nó sẽ được thay đổi trong cả hai danh sách! Đó là điều tôi muốn. Nhưng khi tôi muốn loại bỏ một wafer khỏi danh sách sao chép, nó không nên được loại bỏ khỏi danh sách gốc. Vì vậy, tôi muốn có một danh sách mới các tấm trong mỗi lô nhưng các tham chiếu đến các tấm wafer phải giống như trong lô ban đầu vì tôi muốn thay đổi các giá trị thành các tấm và nó nên thay đổi các giá trị trong wafer ban đầu và sao chép wafer . Có thể không có bản sao sâu?Có thể xóa các mục trong danh sách danh sách mà không mất tham chiếu gốc không?

Tôi có đoạn code sau đây, để giải thích nó tốt hơn:

public class Lot 
    { 
     public string LotName { get; set; } 

     public List<Wafer> Wafers { get; set; } 
    } 

    public class Wafer 
    { 
     public string WaferName { get; set; } 

    } 
    [Test] 
    public void ListInListTest() 
    { 
        //Some Testdata 

     List<Lot> lotList = new List<Lot>(); 
     Lot lot = new Lot(); 
     lot.LotName = "Lot1"; 
     lot.Wafers = new List<Wafer>(); 
     Wafer wafer = new Wafer(); 
     wafer.WaferName = "Wafer1"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer2"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer3"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer4"; 
     lot.Wafers.Add(wafer); 
     lotList.Add(lot); 

     lot = new Lot(); 
     lot.LotName = "Lot1"; 
     lot.Wafers = new List<Wafer>(); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer1"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer2"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer3"; 
     lot.Wafers.Add(wafer); 
     wafer = new Wafer(); 
     wafer.WaferName = "Wafer4"; 
     lot.Wafers.Add(wafer); 
     lotList.Add(lot); 

     //Copy the List 
     List<Lot> copyList = CopyList(lotList); 

     //That works. It removes the lot just in the copyList but not in 
     //the original one 
     copyList.RemoveAt(1); 

     //This works not like i want. It removes the wafers from the copied list 
     //and the original list. I just want, that the list will be changed 
     //in the copied list 
     copyList[0].Wafers.RemoveAt(0); 


    } 

    private List<Lot> CopyList(List<Lot> lotList) 
    { 
     List<Lot> result = new List<Lot>(lotList); 

     foreach (Lot lot in result) 
     { 
      lot.Wafers = new List<Wafer>(lot.Wafers); 
     } 

     return result; 
    } 

Tôi hy vọng nó không phải là quá khó hiểu? Và tôi hy vọng câu hỏi của tôi được giải thích đủ tốt.

+0

bạn cany thử http://stackoverflow.com/questions/1582285/how-to-remove-elements-from-a-generic-list-while-iterating-over-it –

+0

Bạn có thể thử tuần tự hóa và sau đó deserialize mà không có một bản sao sâu – Saravanan

+1

Bạn có chắc chắn rằng nó không hoạt động? Có vẻ như bạn đang làm đúng: tạo danh sách mới các tấm (quan trọng) được điền bởi các tham chiếu hiện có. Nên làm việc. – JeffRSon

Trả lời

2

Theo như tôi thấy, không có cách nào để bạn làm những gì bạn muốn. Dưới đây là mô tả ngắn gọn tại sao:

List<Lot> (phía xa bên trái) chứa tham chiếu đến đối tượng Lot, lần lượt chứa tham chiếu đến cả hai biến thành viên của nó.

enter image description here

Nếu bạn muốn thay đổi đối với LotName và các thành phần của danh sách Wafers được nhân giống như bạn mô tả, giải pháp đơn giản nhất là để chia sẻ một tham chiếu đến đối tượng Lot giữa danh sách:

enter image description here

Bây giờ bạn có thể thấy lý do tại sao không thể sửa đổi các mục trong danh sách Wafers độc lập bằng cách sử dụng giải pháp này - bạn không thể thoát khỏi thực tế là bạn tái sử dụng cùng một tham chiếu đối tượng!

Bằng cách chia sẻ tham chiếu, bạn sẽ mất khả năng kiểm soát sự khác biệt về hành vi. Để đạt được những gì bạn muốn, tôi nghĩ rằng bạn phải làm một bản sao sâu của các trường hợp Lot, và sau đó bạn có thể quản lý việc truyền các thay đổi thông qua một số loại mẫu Observer.

+0

Wow, cảm ơn bạn. Điều đó làm cho mọi thứ rõ ràng hơn cho tôi! Vì vậy, khi tôi gọi "Danh sách mới ()", nó sẽ chỉ tạo một Danh sách mới cho cùng một tham chiếu nhiều. OK .. hơn tôi phải làm bản sao sâu. :/ –

0

Vấn đề là bạn tạo danh sách sao chép bằng cách duy trì tham chiếu với danh sách chính. Bạn nên sử dụng một cái gì đó như:

private List<Lot> CopyList(List<Lot> list) 
{ 
    List<Lot> clone = new List<Lot>(); 

    foreach(Lot l in list) 
    { 
     List<Wafer> wafers = new List<Wafer>(); 
     foreach(Wafer w in l.Wafers) 
     wafers.Add(w); 

     clone.Add(new Lot(){ LotName = l.LotName, Wafers = wafers }); 
    } 

    return clone; 
} 
+0

Không có Wafer mới được tạo ra, có vẻ như. Chỉ các tài liệu tham khảo cần được lưu giữ. – JeffRSon

+0

Không, anh ta không cần tham chiếu đến _Wafers_, vì bạn có thể thấy anh ta muốn sửa đổi danh sách được sao chép mà không tạo hiệu ứng trên danh sách chính. –

+0

Không có điểm nào trong việc tạo các Wafers mới nếu nó đủ để sao chép tham chiếu. – JeffRSon

0

Câu trả lời ngắn gọn: có thể. Tuy nhiên, phương pháp CopyList sẽ giống như thế này

private List<Lot> CopyList(List<Lot> lotList) 
{ 
    List<Lot> result = new List<Lot>(); 

    for (int i = 0; i < lotList.Count; i++) 
    { 
     Lot lot = new Lot(); 
     lot.LotName = lotList[i].LotName; 
     lot.Wafers = new List<Wafer>(lotList[i].Wafers); 
     result.Add(lot); 
    } 

    return result; 
} 

Điều này sẽ tạo danh sách mới và thêm tất cả các miếng đệm hiện có vào đó.

+0

Nhưng nó không hoạt động theo cách tôi đã làm. Khi tôi loại bỏ một wafer từ danh sách sao chép nó loại bỏ nó cũng từ danh sách ban đầu: (... –

+0

Nó không hoạt động. Nếu bạn sao chép/dán nó trong một ứng dụng giao diện điều khiển bạn sẽ thấy hành vi được mô tả – nmat

+0

@JeffRSon theo như tôi có thể thấy ... nó không phải là – James

7

Tôi nghĩ rằng tôi có thể thấy vấn đề của bạn ở đây. Trong số CopyList bạn sao chép danh sách một cách hiệu quả, tức là

lot.Wafers = new List<Wafer>(lot.Wafers); 

Tuy nhiên, điểm quan trọng cần lưu ý ở đây là tài liệu tham khảo đến đối tượng Lot vẫn giống nhau - vì vậy có hiệu quả bạn đang nhân bản & thay thế Wafers tài sản trên danh sách ban đầu là tốt.

Khi bạn gọi

copyList.RemoveAt(1); 

đó là tốt bởi vì bạn đang thao tác một bản sao của Lotdanh sách. Tuy nhiên, khi bạn gọi

copyList[0].Wafers.RemoveAt(0); 

bạn đang sửa đổi Lotdụ đó vẫn đang được tham chiếu bởi cả hai danh sách. Để giải quyết vấn đề bạn sẽ cần phải sao chép các đối tượng Lot bản thân để thay đổi có hiệu quả các tài liệu tham khảo ví dụ

List<Lot> result = new List<Lot>(lotList.Count); 
foreach (Lot item in lotList) 
{ 
    result.Add(new Lot() 
    { 
     LotName = item.LotName, 
     Wafers = new List<Wafer>(item.Wafers) 
    }); 
} 

Kết quả là bạn sẽ mất bản cập nhật tự động trong cả hai danh sách cho đối tượng Lot bản thân nhưng bạn vẫn sẽ giữ nó cho sửa đổi các đối tượng Wafer riêng lẻ (vì tham chiếu cho chúng sẽ không thay đổi).

Vì vậy, để tóm tắt, những gì bạn đang yêu cầu một cách hiệu quả là:

Làm thế nào tôi có thể giữ các tài liệu tham khảo cùng một đối tượng khi có một tài liệu tham khảo hữu khác nhau?

Câu trả lời cho câu hỏi đó là - bạn không thể.

+0

Cảm ơn bạn! Bạn đúng rồi. Điều đó mở ra đôi mắt của tôi ... –

0

Bạn nên sử dụng hai biến riêng biệt cho danh sách, ví dụ: ListLotAListLotB. Nếu bạn muốn có thể xóa Wafer khỏi một danh sách mà không xóa nó khỏi một danh sách khác và đồng thời điều chỉnh Wafer sao cho nó phản ánh trong cả hai danh sách, bạn nên sao chép Wafers từ danh sách này sang danh sách khác, tức là bằng cách sử dụng của một mảng tạm thời.

Đoạn mã ví dụ dưới đây minh họa điều này:

List<Lot> ListLotA = new List<Lot>(); 

Wafer w1 = new Wafer() { WaferName = "w1" }; 
Wafer w2 = new Wafer() { WaferName = "w2" }; 
Wafer w3 = new Wafer() { WaferName = "w3" }; 

ListLotA.Wafers.Add(w1); 
ListLotA.Wafers.Add(w2); 
ListLotA.Wafers.Add(w3); 

List<Lot> ListLotB = new List<Lot>(); 

// At this point adding w1, w2, w3 to the second list is possible, but let's 
// assume it's not and you have to copy them from the first list. 
Wafer[] wArray = new Wafer[ListLotA.Wafers.Count]; 
ListLotA.Wafers.CopyTo(wArray); 

ListLotB.Wafers.AddRange(wArray); 

ListLotAListLotB là những đối tượng riêng biệt, ví dụ chúng có các tham chiếu khác nhau, nhưng danh sách các Wafers riêng lẻ có cùng tham chiếu.

ListLotB.Wafers[1].WaferName = "New Value"; 

Dòng trên sẽ thay đổi Wafer tại chỉ mục 1 trong ListLotB, nhưng cũng sẽ có hiệu lực tương tự trong danh sách 1 trong ListLotA (mặc dù chỉ mục của nó).

ListLotA.Wafers.Remove(w2); 

Điều này sẽ loại bỏ Wafer w2 khỏi ListLotA, chứ không phải từ ListLotB.

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