C# 比较.NET中的GUID时出现意外行为
我试图创建一个如下所示的扩展方法C# 比较.NET中的GUID时出现意外行为,c#,lambda,comparison,extension-methods,guid,C#,Lambda,Comparison,Extension Methods,Guid,我试图创建一个如下所示的扩展方法 public static IEnumerable<T> Distinct<T>(this IEnumerable<T> value, IEnumerable<T> compareTo, Func<T, object> compareFieldPredicate) { return value.Where(o => !compareTo.Exists(p => compareFiel
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> value, IEnumerable<T> compareTo, Func<T, object> compareFieldPredicate)
{
return value.Where(o => !compareTo.Exists(p => compareFieldPredicate.Invoke(p) == compareFieldPredicate.Invoke(o)));
}
IEnumerable<MyCollection> distinctValues = MyCollection.Distinct(MyOtherCollection, o => o.ID); //Note that o.ID is a guid
公共静态IEnumerable Distinct(此IEnumerable值、IEnumerable compareTo、Func compareFieldPredicate)
{
返回值。其中(o=>!compareTo.Exists(p=>compareFieldPredicate.Invoke(p)==compareFieldPredicate.Invoke(o));
}
我的想法是我可以做这样的事情
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> value, IEnumerable<T> compareTo, Func<T, object> compareFieldPredicate)
{
return value.Where(o => !compareTo.Exists(p => compareFieldPredicate.Invoke(p) == compareFieldPredicate.Invoke(o)));
}
IEnumerable<MyCollection> distinctValues = MyCollection.Distinct(MyOtherCollection, o => o.ID); //Note that o.ID is a guid
IEnumerable distinctValues=MyCollection.Distinct(MyOtherCollection,o=>o.ID)//请注意,o.ID是一个guid
现在,在这一点上,我本以为只有我独特的项目返回给我,但我发现,这是从来没有这样的情况
在进一步研究后,使用以下代码分解此方法
Guid guid1 = Guid.NewGuid();
Guid guid2 = new Guid(guid1.ToString());
Func<MyObject, object> myFunction = o => o.ID;
Func<MyObject, object> myFunction1 = o => o.ID;
bool result = myFunction(MyObject) == myFunction1(MyObject);
//result = false
guid1=Guid.NewGuid();
Guid guid2=新Guid(guid1.ToString());
Func myFunction=o=>o.ID;
Func myFunction1=o=>o.ID;
bool result=myFunction(MyObject)==myFunction1(MyObject);
//结果=错误
我发现事实上,即使guid是相同的,比较也总是返回false
这是什么原因
bool result = (guid1==guid2); //result -> true
您可以尝试在myfunction和myfunction1中将返回类型对象更改为GUID
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
Func myFunction=o=>o.ID;
Func myFunction1=o=>o.ID;
否则,返回值(true)将被装箱到对象,并检查引用相等,这是false。更改为使用
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
Func myFunction=o=>o.ID;
Func myFunction1=o=>o.ID;
这是因为你的函数被定义为
Func<MyObject, object>
Func
myFunction
和myFunction1
返回的Guid将被装箱到两个不同的OBEJCT中。有关.NET中的装箱和取消装箱功能,请参见
因此,在进行比较时,会比较两个不同的对象
对象中
Equals
的默认实现是执行引用相等检查。它没有检查装箱的值。有关如何实现object.Equals的更多详细信息,请参阅。如果将lambda更改为返回Guid,则它可以工作:
Func<MyObject, Guid> myFunction = o => o.ID;
Func<MyObject, Guid> myFunction1 = o => o.ID;
Func myFunction=o=>o.ID;
Func myFunction1=o=>o.ID;
您的问题是,在比较GUI之前,您正在将其装箱到对象中。考虑这个代码:
Guid g1 = Guid.NewGuid();
var g2 = g1;
Console.WriteLine(g1 == g2);
object o1 = g1;
object o2 = g2;
Console.WriteLine(o1 == o2);
这实际上产生了:
true
false
因为“o1”和“o2”虽然等于相同的Guid,但不是相同的对象
如果确实希望“独特”扩展方法不绑定到特定类型(如Guid),可以执行以下操作:
public static IEnumerable<TItem> Distinct<TItem, TProp>(this IEnumerable<TItem> value, IEnumerable<TItem> compareTo, Func<TItem, TProp> compareFieldPredicate)
where TProp : IEquatable<TProp>
{
return value.Where(o => !compareTo.Any(p => compareFieldPredicate(p).Equals(compareFieldPredicate(o))));
}
公共静态IEnumerable Distinct(此IEnumerable值、IEnumerable compareTo、Func compareFieldPredicate)
其中TProp:IEquatable
{
返回值。其中(o=>!compareTo.Any(p=>compareFieldPredicate(p).Equals(compareFieldPredicate(o)));
}
正如其他人所说,您的compareFieldPredicate
返回一个对象
,其运算符=
使用对象。ReferenceEquals
,而不是对象。Equals
,因此您的代码总是检查对象标识,而不是相等
解决这个问题的一个方法是使用object.Equals
方法,而不是运算符=
:
public static IEnumerable<T> Distinct<T>(
this IEnumerable<T> value,
IEnumerable<T> compareTo,
Func<T, object> compareFieldPredicate
)
{
return value.Where(o => !compareTo.Exists(
p => object.Equals(compareFieldPredicate(p), compareFieldPredicate(o))
));
}
但是,Distinct
方法的大部分功能已经由LINQ方法 您可以通过提供
IEqualityComparer
的实现来重写实现:
private class KeyEqualityComparer<T, TKey> : IEqualityComparer<T>
{
private readonly Func<T, TKey> _keySelector;
public KeyEqualityComparer(Func<T, TKey> keySelector)
{ _keySelector = keySelector; }
public int GetHashCode(T item)
{ return _keySelector(item).GetHashCode(); }
public bool Equals(T x, T y)
{ return EqualityComparer<TKey>.Default.Equals(_keySelector(x), _keySelector(y)); }
}
public static IEnumerable<T> ExceptBy<T, TKey>(
this IEnumerable<T> first,
IEnumerable<T> second,
Func<T, TKey> keySelector
)
{
return first.Except(second, new KeyEqualityComparer<T, TKey>(keySelector));
}
私有类KeyEqualityComparer:IEqualityComparer
{
专用只读功能键选择器;
公钥平等比较程序(Func密钥选择器)
{u keySelector=keySelector;}
公共int GetHashCode(T项)
{return_keySelector(item).GetHashCode();}
公共布尔等于(TX,TY)
{return EqualityComparer.Default.Equals(_keySelector(x),_keySelector(y));}
}
公共静态IEnumerable ExceptBy(
这是第一次,
我数不清的秒,
Func键选择器
)
{
首先返回。除了(第二个,新的KeyEqualityComparer(keySelector));
}
包含guid类型字段ID的自定义类。抱歉,如果这不清楚…可能我遗漏了什么,但是如果它是一个类型,为什么要将它传递到myFunction
或myFunction1
?这就是我将如何表达我的答案的方式,但是由于两个返回的对象是相同的引用,我不清楚为什么它返回false。但是,将它们更改为Guid会修复它。@Michael由myFunction
和myFunction1
返回的Guid将被装箱到两个不同的对象中。请参阅此链接,非常感谢您提供了简明的答案。我想这可能是类似的,但我没有100%的理解。顺便说一句,Equatable泛型约束是一个非常好的方法。