Предотвращение дублирования списка <T> Записи

Я ожидаю, что смогу сделать работу, но я не могу понять, почему этот код работает неправильно и позволяет добавлять дубликаты в список.

Условие оператора if никогда не выполняется, даже если я перетаскиваю идентичные файлы из одного и того же местоположения. Я не понимаю, почему метод "Содержит" не соответствует им.

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

Я думал, что нашел другой способ достижения этого, используя "Distinct"

Однако, как представляется, checkedDragDropFiles и dragDropFiles имеют одинаковое количество записей, включая дубликаты, за исключением случаев, когда dragDropFiles отображается в ListBox, он не показывает их. Почему это делается?

Мне нужно предотвратить любые дублированные записи в списке, поскольку я бы программно создавал меню, основанное на данных списка.

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

Ответ 1

List<T> действительно разрешает дубликаты.

В случае FileInfo метод Contains будет проверять, являются ли ссылки одинаковыми, но по мере того, как вы извлекаете полностью новый набор FileInfo, ссылки различаются.

Вам нужно использовать перегрузку Contains, которая принимает IEqualityComparer - см. здесь.

Вы также можете использовать HashSet<T> - это структура данных, которая не позволяет дублировать (хотя с разными ссылками вы все равно есть эта проблема).

Ответ 2

Поскольку реализация по умолчанию Object.Equals сравнивает объекты по ссылке, а не по значению. Каждый экземпляр FileInfo, который вы создаете, представляет собой другой объект, что касается .NET.

Вы можете использовать LINQ, чтобы указать свой собственный предикат сравнения, чтобы сравнивать объекты по другому свойству:

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

[изменить]

Так как строки сравниваются по значению, вы можете также фильтровать список до, вы проецируете его на FileInfo, например:

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

Ответ 3

Вы можете легко создать несколько экземпляров FileInfo для одного и того же файла - поэтому ваш список будет содержать каждую FileInfo только один раз, но он может содержать несколько файлов FileInfos для файла smae.

Таким образом, лучшим вариантом может быть использование Hashtable и использование параметра FileInfo.FullName в качестве критерия.

Ответ 4

Если вы хотите, чтобы реализация ICollection<T>, которая не позволяет дублировать, сохраняя при этом порядок, рассмотрите SortedSet<T>, а не List<T>.