2012-01-11 6 views
11

Espero poder solucionar el problema, pero no puedo entender por qué este código no funciona correctamente y permitir que se agreguen entradas duplicadas a la lista.Prevención de la lista duplicada <T> Entradas

La condición de enunciado if nunca se cumple, incluso cuando arrastro archivos idénticos desde la misma ubicación. No entiendo por qué el método "Contiene" no los está combinando.

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

pensé que había encontrado otro método en el que para lograr esto usando "Distinto"

Sin embargo, parece checkedDragDropFiles & dragDropFiles tienen la misma cantidad de entradas, incluyendo los duplicados, excepto cuando se muestra dragDropFiles en un ListBox, no se muestran. ¿Por qué hace esto?

Necesito evitar cualquier entrada de lista duplicada, ya que crearía programáticamente un menú basado en los datos de la lista.

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

lo que hace 'FileInfo's la misma, tal vez debería implementar un' 'IEqualityComparer para pasar a' Distinct' – Jodrell

+0

Sólo una nota: Si 'Contains' vuelve _true_, ¿por qué quitar y agregar? Realice una comprobación negativa y solo agregue si la lista no _no_ contiene el valor. – Oded

+0

Oded: Buen punto, eso es un poco una acción desperdiciada. – negligible

Respuesta

18

List<T> de hecho permite duplicados.

En el caso de FileInfo, el método Contains será comprobar si las referencias son los mismos, pero a medida que esté recogiendo una forma completamente nueva conjunto de FileInfo, las referencias son diferentes.

Necesita usar la sobrecarga de Contains que toma un IEqualityComparer - vea here.

En su lugar, también puede usar HashSet<T> - es una estructura de datos que no permite duplicados (aunque con referencias diferentes, todavía tendrá este problema).

+0

En este caso, eso no ayudaría ¿no? (FileInfo se compara por referencia, no por valor). –

+3

'HashSet ' es bueno, ya que no arroja una excepción si el elemento ya está presente ... ¡así! –

+1

@JeffFoster estoy seguro de que puedes usar un contenedor, mejorar este contenedor con 'IEtableable ', anular '.Equals' y' .GetHashCode' y 'HashSet ' debería funcionar –

6

Porque la implementación predeterminada Object.Equals compara objetos por referencia, no por valor. Cada instancia FileInfo que crees es un objeto diferente, en lo que se refiere a .NET.

Puede utilizar LINQ para especificar su comparación predicado personalizada con el fin de comparar objetos de diferente propiedad:

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

[Editar]

Dado que las cadenas se comparan por su valor, puede ser que también filtrar la lista antes proyectas a FileInfo, así:

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

largo tiempo desde que he visto' if (archivosDrrowDropFiles.Any (f => f.Name ==) == falso) ' ... :) 'if (! dragDropFiles.Any (f => f.Name == file))' –

+0

@Andreas: Esta es la forma en que lo hacíamos en los viejos tiempos. : Es solo una broma. En realidad, solo lo hice para enfatizar que invertí la lógica de OP (agregar a la lista ahora se hace dentro del condicional). – Groo

+0

:) bueno ... un '!' Podría perderse fácilmente –

0

Puede crear fácilmente varias instancias de FileInfo para el mismo archivo, por lo que su lista contendrá cada FileInfo solo una vez, pero puede tener varios FileInfos para el archivo smae.

Así que su mejor opción podría ser utilizar una Hashtable y usar FileInfo.FullName como criterio.

0

Si desea una implementación de ICollection<T> que no permita duplicados, al tiempo que conserva un pedido, considere usar SortedSet<T> en lugar de List<T>.

Cuestiones relacionadas