问题描述
我有一个带有哈希集的类作为集合。有时,我将此哈希集作为参数传递给方法,但参数为IEnumerable
。
在这种方法中,我想检查对象的参数和属性是否为同一集合,以避免在集合相同的情况下执行任何操作。
班级是这个:
MyClass
{
HashSet<MyType> MyCollection;
public void MyMethod(IEnumerable<MyType> param)
{
if(this.MyCOllecction != param)
{
this.MyCollection.Clear();
this.MyCollection.AddRange(param);
}
}
}
问题在于if是否为true,因此它将处理集合。问题在于最终结果是MyCollection
为空,因为首先我清除了集合,所以当IEnumerable
参数尝试访问集合中的元素时,它为空,因此最终结果是MyCollection
为空。应该再次添加MyCollection
拥有的元素。
我想知道如何知道IEnumerable
是否将访问MyCollection
属性或另一个不同的集合。
谢谢
解决方法
不可能可靠地优化此方法,因为无法确定传递给该方法的集合是否与该类所持有的集合相同。
您可能会认为您可以使用object.Equals()
进行检查,这确实适用于某些情况-但并非适用于所有情况。考虑以下代码:
HashSet<int> hashset = new HashSet<int>();
IEnumerable<int> test1 = hashset;
IEnumerable<int> test2 = hashset;
// Prints "equal"
Console.WriteLine(object.Equals(test1,test2) ? "equal" : "not equal");
test1 = hashset.AsEnumerable();
test2 = hashset.AsEnumerable();
// Prints "equal"
Console.WriteLine(object.Equals(test1,test2) ? "equal" : "not equal");
test1 = hashset;
test2 = hashset.Select(item => item);
// Prints "not equal". Oh dear.
Console.WriteLine(object.Equals(test1,test2) ? "equal" : "not equal");
因此,这表明仅基于对象引用相等性就不可能可靠地对其进行优化。
此外,您声明元素数量很少-因此不需要优化。
如果您真的想安全地优化此操作,则可以执行以下操作:
public void MyMethod(IEnumerable<MyType> param)
{
if (!object.Equals(this.MyCollection,param))
{
this.MyCollection = new HashSet<MyType>();
this.MyCollection.AddRange(param);
}
// ...
}
但是我认为这种优化并不是真正必要的,因此您可以将其实现为:
public void MyMethod(IEnumerable<MyType> param)
{
this.MyCollection = new HashSet<MyType>();
this.MyCollection.AddRange(param);
// ...
}
您将必须在代码中使用实际类型,而不要使用没有HashSet
方法的AddRange()
。您问题中的代码显然不是您的实际代码...
问题尚不清楚-same
可能表示相同的实例或包含相同的数据。原始代码无论如何都无法正常工作,因为HashSet
没有AddRange
。
替换存储集
处理此问题的一种可能方法是使用模式匹配来检查输入是否为HashSet
,且该实例与存储的实例相同:
public void MyMethod(IEnumerable<MyType> param)
{
if(param is HashSet<MyType> h && h.ReferenceEquals(MyCollection))
{
return;
}
MyCollection=new HashSet<MyType>(param);
}
合并新值
HashSet
没有AddRange
,因此原始代码仍然无法正常工作。等效为UnionWith,它将添加 only 个新项目。这比使用SequenceEquals
然后添加新项目要快,因为HashSet已针对集合操作进行了优化:
public void MyMethod(IEnumerable<MyType> param)
{
if(param is HashSet<MyType> h && h.ReferenceEquals(MyCollection))
{
return;
}
MyCollection.UnionWith(param);
}