C# Hashset未添加重复项,但为Add()返回true

C# Hashset未添加重复项,但为Add()返回true,c#,C#,(编辑)更多信息。第一个通知是“新虚拟”。这个类继承了一个基类,这个基类应该是一个通用的父类,可以用任何ICollection类型创建。这是描述词,基本上是: public abstract class ParentAwareCollection<TObject, TParent, TInnerList> : ICollection<TObject>, ICollection, IParentProvider<TParent> where TObje

(编辑)更多信息。第一个通知是“新虚拟”。这个类继承了一个基类,这个基类应该是一个通用的父类,可以用任何ICollection类型创建。这是描述词,基本上是:

public abstract class ParentAwareCollection<TObject, TParent, TInnerList> :
 ICollection<TObject>, ICollection, IParentProvider<TParent>
    where TObject : IParentProvider<TParent>
    where TInnerList : ICollection<TObject>, new()
{
        protected TInnerList InnerList = new TInnerList();
...
}
请注意,在中实现的ICollection与我的一样。这就是为什么我相信这样做是可以的。但是,与我的不同,框架HashSet没有为其Add()方法使用
new
描述符,但这是必需的,因为
ICollection
实现了
void Add(T项)
。也许他们只是漏掉了

原始问题:

我有一个collection类,它使用
HashSet
存储其对象。类型为T的对象覆盖
GetHashCode()
,其中它们确保存在用于生成哈希代码的某些必需信息。我不确定这是否重要

当我向HashSet添加(T)时,它不会添加对象,而是返回true。如果我调试,然后在即时窗口中再次尝试添加它,它将返回false,就像它应该的那样。方法如下所示:

public new virtual bool Add(TObject item)
{
    // Must add parent first, since it may be used in the hash code
    // InnerList is a HashSet<T>

    if (InnerList.Any(existing=>item.GetHashCode()==existing.GetHashCode())) {
        return(false);
    } else {
        if (InnerList.Add(item))
        {
            return(true);
        } else {
            return(false);
        }
    }
}
public new virtual bool Add(TObject item)
{
    return InnerList.Add(item);
}
单步遍历InnerList.Add(),它返回true:

?InnerList.Count
1
?InnerList.Add(item)
false

Wtf?这是.net 4.0 framework。

您的
添加的
代码错误:

public new virtual bool Add(TObject item)
{
    // Must add parent first, since it may be used in the hash code
    // InnerList is a HashSet<T>

    if (InnerList.Any(existing=>item.GetHashCode()==existing.GetHashCode())) {
        return(false);
    } else {
        if (InnerList.Add(item))
        {
            return(true);
        } else {
            return(false);
        }
    }
}

您的
添加
代码错误:

public new virtual bool Add(TObject item)
{
    // Must add parent first, since it may be used in the hash code
    // InnerList is a HashSet<T>

    if (InnerList.Any(existing=>item.GetHashCode()==existing.GetHashCode())) {
        return(false);
    } else {
        if (InnerList.Add(item))
        {
            return(true);
        } else {
            return(false);
        }
    }
}

我知道了。这是一件非常阴险的小事。因此,这个设置的全部要点是,我可以拥有一个具有“父级”的集合类,当您向集合中添加项时,它们会自动分配给该父级。(这个父对象不仅仅是类本身,它是另一个对象)

问题是:

        CsmResourceHashSet resources = new CsmResourceHashSet(Context);
        resources.AddFrom(Context.ScriptResources
            .Where(item=>item.Enabled));
CsmResourceHashSet
是一个
parentawarhashset
。扩展方法AddFrom:

public static void AddFrom<T>(this ICollection<T> destList, IEnumerable<T> sourceList)
{
    foreach (T obj in sourceList)
    {
        destList.Add(obj);
    }
}

一旦我这么做了,它就起作用了。嗯,实际上它不起作用,但它揭示了一对疯狂的小错误,它们大多没有表现出来,但我所有的异常开始到处爆发,而且没有花时间修复它们。

我找到了答案。这是一件非常阴险的小事。因此,这个设置的全部要点是,我可以拥有一个具有“父级”的集合类,当您向集合中添加项时,它们会自动分配给该父级。(这个父对象不仅仅是类本身,它是另一个对象)

问题是:

        CsmResourceHashSet resources = new CsmResourceHashSet(Context);
        resources.AddFrom(Context.ScriptResources
            .Where(item=>item.Enabled));
CsmResourceHashSet
是一个
parentawarhashset
。扩展方法AddFrom:

public static void AddFrom<T>(this ICollection<T> destList, IEnumerable<T> sourceList)
{
    foreach (T obj in sourceList)
    {
        destList.Add(obj);
    }
}

一旦我这么做了,它就起作用了。嗯,实际上它不起作用,但它揭示了一对疯狂的小错误,它们大多没有表现出来,但我所有的异常开始到处爆发,而且没有时间修复它们。

也许你可以发布一个“准备运行”的代码示例,允许重现这个问题?否则很难猜测问题在哪里……TObject的定义是什么?我们如何复制它?有很多代码。。。我会把基类放上去。请查看我的编辑。不过,我并不完全理解它,因为我正在调用InnerList.Add,所以我不明白为什么基类Add会被调用,而且它没有返回,我将尝试几件事情……请注意,您不应该假设哈希代码是唯一的。您应该依次覆盖
GetHashCode
Equals
<可以说,code>GetHashCode
是验证的第一项。如果它们不匹配,则假设
等于
也将是错误的。但由于
GetHashCode
不能保证唯一性,匹配的哈希代码并不意味着等于真。简言之,哈希集将首先检查哈希代码,如果合适,检查
等于
,并且您也不应该仅仅基于哈希代码匹配而拒绝
添加
。@Jamiere:Gotcha。你不会从多个线程调用这个方法吧?也许你可以发布一个“ready to run”(准备运行)的代码示例来重现这个问题?否则很难猜测问题在哪里……TObject的定义是什么?我们如何复制它?有很多代码。。。我会把基类放上去。请查看我的编辑。不过,我并不完全理解它,因为我正在调用InnerList.Add,所以我不明白为什么基类Add会被调用,而且它没有返回,我将尝试几件事情……请注意,您不应该假设哈希代码是唯一的。您应该依次覆盖
GetHashCode
Equals
<可以说,code>GetHashCode
是验证的第一项。如果它们不匹配,则假设
等于
也将是错误的。但由于
GetHashCode
不能保证唯一性,匹配的哈希代码并不意味着等于真。简言之,哈希集将首先检查哈希代码,如果合适,检查
等于
,并且您也不应该仅仅基于哈希代码匹配而拒绝
添加
。@Jamiere:Gotcha。您不会从多个线程调用此方法吧?正如我所说,我添加了第一个检查,因为它不适用于InnerList.Add()。问题是HashSet.Add()正在添加并返回true,即使它没有添加项。我确实意识到我也需要检查Equals,但这只会导致误判,而不是我遇到的问题。快速查看
HashSet。使用reflector添加
验证这是不可能的。可能是其他线程在检查之前删除了该项,或者是调试器在耍花招-尝试打印是的,这不是HashSet的错,但这里有点让人困惑,请参阅我的答案。正如我所说,我添加了第一个检查,因为它不只是与InnerList.Add()一起工作。问题是HashSet.Add()正在添加并返回true,即使它没有添加项。我确实意识到我也需要检查Equals,但这只会导致误判,而不是我遇到的问题。快速查看
HashSet.Addpublic static void AddFrom<T>(this ISet<T> destList, IEnumerable<T> sourceList)
{
    foreach (T obj in sourceList)
    {
        destList.Add(obj);
    }
}