2012-01-11 15 views
11

Mi aspetto che riuscirò a risolvere il problema ma non riesco a capire perché questo codice non funzioni correttamente e che consenta l'aggiunta di voci duplicate all'elenco.Preventing Duplicate List <T> Entries

La condizione dell'istruzione if non viene mai soddisfatta, anche quando trascino file identici dalla stessa posizione. Non capisco perché il metodo "Contiene" non li combini.

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 { } 
    } 
} 

pensavo di aver trovato un altro metodo in cui per raggiungere questo obiettivo utilizzando "Distinto"

Tuttavia, sembra checkedDragDropFiles & dragDropFiles hanno la stessa quantità di voci, tra cui i duplicati, ad eccezione quando dragDropFiles viene visualizzato in un ListBox non li mostra. Perché fa questo?

Devo evitare voci di lista duplicate, poiché creo un menu in base ai dati dell'elenco.

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

ciò che rende 'FileInfo's lo stesso, forse dovresti implementare un' IEqualityComparer 'per passare a' Distinct' – Jodrell

+0

Solo una nota: Se 'Contains' restituisce _true_, perché rimuovere e aggiungere? Esegui un controllo negativo e aggiungi solo se l'elenco non contiene il valore _non_. – Oded

+0

Odiato: buon punto, è piuttosto un'azione sprecata. – negligible

risposta

18

List<T> consente effettivamente duplicati.

Nel caso della FileInfo, il metodo Contains sarà verificare se i riferimenti sono gli stessi, ma come stai recuperando un nuovo serie completamente di FileInfo, i riferimenti sono diversi.

È necessario utilizzare il sovraccarico di Contains che accetta uno IEqualityComparer - vedere here.

È inoltre possibile utilizzare HashSet<T> - è una struttura dati che non consente duplicati (anche se con riferimenti diversi, si avrà ancora questo problema).

+0

In questo caso, non sarebbe d'aiuto? (FileInfo viene confrontato per riferimento, non valore). –

+3

'HashSet ' è bello, in quanto non genera un'eccezione se l'elemento è già presente ... così! –

+1

@JeffFoster ho la certezza che è possibile utilizzare un wrapper, valorizzare questo wrapper con 'IEquatable ', sovrascrivere '.Equalisti 'e' .GetHashCode' e 'HashSet ' dovrebbe funzionare –

6

Poiché l'implementazione di default Object.Equals confronta gli oggetti per riferimento, non per valore. Ogni istanza di FileInfo creata è un oggetto diverso, per quanto riguarda .NET.

È possibile utilizzare LINQ per specificare il confronto predicato personalizzato al fine di confrontare gli oggetti da differenti proprietà:

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

[Edit]

Poiché le stringhe vengono confrontate per valore, si potrebbe anche filtrare l'elenco prima si proietta a FileInfo, in questo modo:

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

molto tempo da quando ho visto 'if (dragDropFiles.Any (f => f.Nome == file) == false)' ... :) 'if (! dragDropFiles.Any (f => f.Nome == file))' –

+0

@Andreas: Questo è il modo in cui lo abbiamo fatto ai vecchi tempi. MrGreen Sto scherzando. In realtà l'ho fatto solo per sottolineare che ho invertito la logica dell'OP (aggiungendo alla lista ora è fatto il condizionale). – Groo

+0

:) beh ... un '!' Potrebbe facilmente essere perso –

0

È possibile creare facilmente più istanze FileInfo per lo stesso file, quindi l'elenco conterrà ogni FileInfo una sola volta, ma potrebbe avere più FileInfos per il file smae.

Quindi la soluzione migliore potrebbe essere utilizzare un Hashtable e utilizzare FileInfo.FullName come criterio.

0

Se si desidera un'implementazione di ICollection<T> che non consente duplicati, pur mantenendo un ordine, considerare l'utilizzo di SortedSet<T> anziché di List<T>.

Problemi correlati