Предотвращение дублирования списка <T>записей - PullRequest
11 голосов
/ 11 января 2012

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

Условие оператора 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 { }
}

Ответы [ 4 ]

18 голосов
/ 11 января 2012

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

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

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

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

6 голосов
/ 11 января 2012

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

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

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

[Edit]

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

private void OutputDragDrop(string[] files)
{
    dragDropFiles = files.Distinct().Select(f => new FileInfo(f)).ToList();
    debugList.DataSource = checkedDragDropFiles;
    debugList2.DataSource = dragDropFiles;
}
0 голосов
/ 11 января 2012

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

0 голосов
/ 11 января 2012

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

Поэтому лучше всего использоватьHashtable и используйте FileInfo.FullName в качестве критерия.

...