2012-01-11 44 views
11

Tôi hy vọng tôi sẽ có thể thực hiện công việc nhưng tôi không thể cho cuộc sống của tôi hiểu tại sao mã này không hoạt động chính xác và cho phép nhập các mục trùng lặp vào Danh sách.Ngăn danh sách trùng lặp <T> Entries

Điều kiện tuyên bố if không bao giờ được đáp ứng, ngay cả khi tôi kéo các tệp giống hệt nhau từ cùng một vị trí. Tôi không hiểu tại sao phương pháp "Chứa" không phù hợp với chúng.

public class Form1:Form { 
    private List<FileInfo> dragDropFiles = new List<FileInfo>(); 

    private void Form1_DragDrop(object sender, DragEventArgs e) { 
     try { 
      if (e.Data.GetDataPresent(DataFormats.FileDrop)) { 
       string[] files = 
        (string[])e.Data.GetData(DataFormats.FileDrop); 

       OutputDragDrop(files); 
      } 
     } 
     catch { } 
    } 

    private void Form1_DragEnter(object sender, DragEventArgs e) { 
     if (e.Data.GetDataPresent(DataFormats.FileDrop)) 
      e.Effect = DragDropEffects.Copy; 
     else 
      e.Effect = DragDropEffects.None; 
    } 

    private void OutputDragDrop(string[] files) { 
     try { 
      foreach (string file in files) { 
       FileInfo fileInfo = new FileInfo(file); 

       if (dragDropFiles.Contains(fileInfo)) { 
        dragDropFiles.Remove(fileInfo); 
       } 
       dragDropFiles.Add(fileInfo); 
      } 
      PopulateContextMenu(); 
     } 
     catch { } 
    } 
} 

tôi nghĩ rằng tôi đã tìm thấy một phương pháp trong đó để đạt được điều này bằng "biệt"

Tuy nhiên, dường checkedDragDropFiles & dragDropFiles có cùng một lượng mục, trong đó có bản sao, ngoại trừ khi dragDropFiles được hiển thị trong một ListBox, nó không hiển thị chúng. tại sao nó làm vậy?

Tôi cần phải ngăn chặn bất kỳ mục nhập danh sách trùng lặp nào, vì tôi sẽ lập trình tạo trình đơn dựa trên dữ liệu danh sách.

private void OutputDragDrop(string[] files) 
{ 
    try 
    { 
     foreach (string file in files) 
     { 
      FileInfo fileInfo = new FileInfo(file); 

      //if (dragDropFiles.Contains(fileInfo)) 
      //{ 
      // dragDropFiles.Remove(fileInfo); 
      //} 
      dragDropFiles.Add(fileInfo); 
     } 

     List<FileInfo> checkedDragDropFiles = dragDropFiles.Distinct().ToList(); 

     debugList.DataSource = checkedDragDropFiles; 
     debugList2.DataSource = dragDropFiles; 
     //PopulateContextMenu(); 
    } 
    catch { } 
} 
+2

điều gì làm cho 'FileInfo' giống nhau, có lẽ bạn nên triển khai một 'IEqualityComparer ' để chuyển đến 'Distinct' – Jodrell

+0

Chỉ cần lưu ý: Nếu 'Contains' returns _true_, tại sao xóa và thêm? Thực hiện kiểm tra âm và chỉ thêm nếu danh sách không _not_ chứa giá trị. – Oded

+0

Oded: Điểm tốt, đó là kinda một hành động lãng phí. – negligible

Trả lời

18

List<T> thực sự cho phép trùng lặp.

Trong trường hợp của FileInfo, phương pháp Contains sẽ được kiểm tra xem tài liệu tham khảo là như nhau, nhưng khi bạn đang lấy một mới tập hoàn toàn của FileInfo, tài liệu tham khảo khác nhau.

Bạn cần sử dụng quá tải Contains mất IEqualityComparer - xem here.

Bạn cũng có thể sử dụng HashSet<T> để thay thế - đó là cấu trúc dữ liệu không cho phép trùng lặp (mặc dù có các tham chiếu khác nhau, bạn vẫn sẽ gặp sự cố này).

+0

Trong trường hợp này sẽ không hữu ích? (FileInfo được so sánh bằng tham chiếu, không phải giá trị). –

+3

'HashSet ' là tốt đẹp, vì nó không ném một ngoại lệ nếu phần tử đã có mặt ... như thế! –

+1

@ JeffFoster tôi shure rằng bạn có thể sử dụng một wrapper, tăng cường wrapper này với 'IEquatable ', ghi đè '.Equals' và' .GetHashCode' và 'HashSet ' nên làm việc –

6

Bởi vì thực thi Object.Equals mặc định so sánh các đối tượng theo tham chiếu, chứ không phải theo giá trị. Mỗi cá thể FileInfo bạn tạo là một đối tượng khác, theo như .NET có liên quan.

Bạn có thể sử dụng LINQ để xác định vị so sánh tùy chỉnh của bạn để so sánh đối tượng bằng tài sản khác nhau:

if (dragDropFiles.Any(f => f.Name == file) == false) 
{ 
    dragDropFiles.Add(fileInfo); 
} 

[Chỉnh sửa]

Kể từ khi chuỗi được so sánh theo giá trị, bạn cũng có thể lọc danh sách trước bạn chiếu nó lên FileInfo, như sau:

private void OutputDragDrop(string[] files) 
{ 
    dragDropFiles = files.Distinct().Select(f => new FileInfo(f)).ToList(); 
    debugList.DataSource = checkedDragDropFiles; 
    debugList2.DataSource = dragDropFiles; 
} 
+0

thời gian dài kể từ khi tôi đã thấy' if (dragDropFiles.Any (f => f.Name == file) == false) ' ... :) 'if (! dragDropFiles.Any (f => f.Name == file))' –

+0

@Andreas: Đây là cách chúng ta đã làm nó trong những ngày cũ. :-D Chỉ đùa thôi. Trên thực tế tôi chỉ làm điều đó để nhấn mạnh rằng tôi đảo ngược logic của OP (thêm vào danh sách bây giờ được thực hiện bên trong điều kiện). – Groo

+0

:) cũng ... một '!' Có thể dễ dàng bỏ qua –

0

Bạn có thể dễ dàng tạo nhiều phiên bản FileInfo cho cùng một tệp - do đó danh sách của bạn sẽ chứa mọi FileInfo chỉ một lần, nhưng nó có thể có nhiều FileInfos cho tệp smae.

Vì vậy, đặt cược tốt nhất của bạn có thể là sử dụng một Hashtable và sử dụng FileInfo.FullName làm tiêu chí.

0

Nếu bạn muốn triển khai ICollection<T> không cho phép trùng lặp, trong khi vẫn giữ lại đơn đặt hàng, hãy xem xét sử dụng SortedSet<T> thay vì List<T>.

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