【问题标题】:Preventing Duplicate List<T> Entries防止重复的 List<T> 条目
【发布时间】:2012-02-07 19:06:44
【问题描述】:

我希望我能够解决问题,但我一生都无法理解为什么此代码无法正常运行并允许将重复条目添加到列表中。

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”实现此目的的方法

但是,checkedDragDropFilesdragDropFiles 似乎具有相同数量的条目,包括重复项,除非 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 { }
}

【问题讨论】:

  • 是什么让FileInfos 相同,也许你应该实现一个IEqualityComparer&lt;FileInfo&gt; 来传递给Distinct
  • 请注意:如果Contains 返回true,为什么要删除和添加?进行否定检查,仅在列表不包含该值时添加。
  • Oded:说得好,这有点浪费。

标签: c# list collections duplicates


【解决方案1】:

List&lt;T&gt; 确实允许重复。

FileInfo 的情况下,Contains 方法将检查引用是否相同,但是当您获取一组完全FileInfo 时,引用是不同。

您需要使用采用IEqualityComparerContains 的重载 - 请参阅here

您也可以改用HashSet&lt;T&gt; - 它是一种不允许重复的数据结构(尽管使用不同的引用,您仍然会遇到这个问题)。

【讨论】:

  • 在这种情况下不会有帮助吧? (FileInfo 通过引用进行比较,而不是值)。
  • HashSet&lt;T&gt; 很好,因为如果元素已经存在,它不会抛出异常......就像那样!
  • @JeffFoster - 非常正确。已经在更新我的答案了。
  • 对于性能问题我不会使用List&lt;T&gt;。为什么?在每次迭代中,您执行.Contains.Any(缺少基于哈希的比较)-“最佳”情况:每次迭代都会添加一个项目...为什么不创建Dictionary&lt;string, FileInfo&gt;,其中的关键是(不变和忽略大小写)表示例如FullName 属性。这将提高性能,因为.ContainsKey 将执行基于哈希的检查(而List&lt;T&gt;-解决方案缺少这种检查)。之后你只需使用.Values-property...
【解决方案2】:

因为默认的Object.Equals 实现通过引用而不是值来比较对象。就 .NET 而言,您创建的每个 FileInfo 实例都是不同的对象。

您可以使用 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;
}

【讨论】:

  • 很久没看到if (dragDropFiles.Any(f =&gt; f.Name == file) == false) ... :) if (!dragDropFiles.Any(f =&gt; f.Name == file))
  • @Andreas:这是我们过去的做法。 :-D 开个玩笑。实际上我这样做只是为了强调我颠倒了 OP 的逻辑(添加到列表现在是在条件内完成的)。
  • :) 好吧...! 可能很容易被遗漏
  • 注意:这可能适用于这种特殊情况 - 但在其他情况下,我宁愿进行不变量和忽略大小写的比较。这可以通过将System.StringComparer.CurrentCultureIgnoreCaseSystem.StringComparer.InvariantCultureIgnoreCase 传递给.Distinct 来轻松完成
  • @AndreasNiedermair:AFAIK,两个文件名不能位于同一个文件夹中,并且仅在大小写上有所不同,但如果文件名有可能得到不同的大小写,那么我同意,它会更安全。
【解决方案3】:

您可以轻松地为同一个文件创建多个 FileInfo 实例 - 因此您的列表将只包含每个 FileInfo 一次,但它可能有多个 FileInfo 用于 smae 文件。

因此,您最好的选择可能是使用 Hashtable 并使用 FileInfo.FullName 作为标准。

【讨论】:

    【解决方案4】:

    如果您想要一个不允许重复的ICollection&lt;T&gt; 实现,同时仍保留顺序,请考虑使用SortedSet&lt;T&gt; 而不是List&lt;T&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-29
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多