Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/257.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么要收集<>;。Contains忽略我覆盖的等号和IEquatable<&燃气轮机;接口?_C#_Entity Framework_Entity Framework 6_Equals_C# 6.0 - Fatal编程技术网

C# 为什么要收集<>;。Contains忽略我覆盖的等号和IEquatable<&燃气轮机;接口?

C# 为什么要收集<>;。Contains忽略我覆盖的等号和IEquatable<&燃气轮机;接口?,c#,entity-framework,entity-framework-6,equals,c#-6.0,C#,Entity Framework,Entity Framework 6,Equals,C# 6.0,我对实体框架项目中的导航属性有问题 以下是MobileUser的课程: [DataContract] [Table("MobileUser")] public class MobileUser: IEquatable<MobileUser> { // constructors omitted.... /// <summary> /// The primary-key of MobileUser. /// This is not the V

我对实体框架项目中的导航属性有问题

以下是MobileUser的
课程:

[DataContract]
[Table("MobileUser")]
public class MobileUser: IEquatable<MobileUser>
{
    // constructors omitted....

    /// <summary>
    /// The primary-key of MobileUser.
    /// This is not the VwdId which is stored in a separate column
    /// </summary>
    [DataMember, Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }

    [DataMember, Required, Index(IsUnique = true), MinLength(VwdIdMinLength), MaxLength(VwdIdMaxLength)]
    public string VwdId { get; set; }

    // other properties omitted ...

    [DataMember]
    public virtual ICollection<MobileDeviceInfo> DeviceInfos { get; private set; }

    public bool Equals(MobileUser other)
    {
        return this.UserId == other?.UserId || this.VwdId == other?.VwdId;
    }

    public override bool Equals(object obj)
    {
        if(object.ReferenceEquals(this, obj))return true;
        MobileUser other = obj as MobileUser;
        if (other == null) return false;
        return this.Equals(other);
    }

    public override int GetHashCode()
    {
        // ReSharper disable once NonReadonlyMemberInGetHashCode
        return VwdId.GetHashCode();
    }

    public override string ToString()
    {
        return "foo"; // omitted actual implementation
    }

    #region constants
    // irrelevant
    #endregion
}
如您所见,它实现了
IEquatable
,并覆盖
Equals
GetHashCode
中的
System.Object

我有以下测试,我希望
Contains
会调用我的
Equals
,但它没有。它似乎使用了
对象。ReferenceEquals
,因此找不到我的设备,因为它是一个不同的引用:

var userRepo = new MobileUserRepository((ILog)null);
var deviceRepo = new MobileDeviceRepository((ILog)null);

IReadOnlyList<MobileUser> allUser = userRepo.GetAllMobileUsersWithDevices();
MobileUser user = allUser.First();

IReadOnlyList<MobileDeviceInfo> allDevices = deviceRepo.GetMobileDeviceInfos(user.VwdId, true);
MobileDeviceInfo device = allDevices.First();
bool contains = user.DeviceInfos.Contains(device);
bool anyEqual = user.DeviceInfos.Any(x => x.DeviceToken == device.DeviceToken);
Assert.IsTrue(contains); // no, it's false
那么,为什么只比较引用,而忽略我的自定义
Equals

更新

更令人困惑的是,即使我将其转换为
HashSet

而标准的
HashSet
使用:

GenericEqualityComparer<T>

从EF源代码中,您可能会偶然发现,这似乎是连接导航属性的一部分

如果与属性兼容,则调用并返回一个
HashSet
作为类型

然后,使用
HashSet
,它进行调用,根据代码和描述,将
HashSet
作为特例处理,并将其传递给构造函数

因此:EF为您创建的
HashSet
EF没有使用等式实现,而是使用引用等式

为什么ICollection.Contains会忽略我覆盖的Equals和IEquatable接口

因为接口的实现者不需要这样做

ICollection.包含方法MSDN状态:

确定ICollection是否包含特定值

然后

备注

实现在如何确定对象的相等性方面可能有所不同;例如,List使用Comparer.Default,而Dictionary允许用户指定用于比较键的IComparer实现

旁注:看起来他们把
IComparer
iQualityComparer
搞砸了,但你明白了:)

结论:如果您不知道将使用什么比较器,请不要使用Contains,或者使用Enumerable.Contains和接受自定义比较器的重载

根据
Enumerable.Contains(IEnumerable,T)
方法重载(即没有自定义比较器):

使用默认值相等比较器确定序列是否包含指定元素

这听起来像是将调用您的覆盖。但随之而来的是:

备注
如果源类型实现ICollection,则调用该实现中的Contains方法以获得结果。否则,此方法将确定源是否包含指定的元素

这与最初的陈述相冲突


真是一团糟。我只能说,我完全同意这个结论

@TimSchmelter:所以如果它只是一个
散列集
,你应该能够在不涉及EF的情况下复制它,对吗?A在这里会很有帮助。@TimSchmelter比较
((HashSet)user.deviceinfo).Comparer
新的HashSet(user.deviceinfo).Comparer
,我打赌您会看到第一个是使用EF创建的自定义比较器,该比较器忽略您的
iequatable
,第二个是使用默认比较器,该比较器将使用
iequatable
。您可能会发现,这是埋在
哈希集中的相等比较器的问题。你可能需要调试才能找到…@JonSkeet(和其他人):谢谢,你说得对。它实际上使用了一个不同的比较器,entity framework使用了一个名为自我解释的
System.Data.entity.Infrastructure.ObjectReferenceEqualityComparer
。我仍然不明白这个陷阱的原因。我喜欢(具有讽刺意味的是)即使是最好的问题也是如何让选票下降的。在这个网站上提问太难了!谢谢虽然我真的不明白为什么实体框架要在这种情况下使用
ObjectReferenceEqualityComparer
。这是一个令人讨厌的陷阱。@TimSchmelter我相信它正在使用它,因为它希望
ICollection
具有快速缓存查找,它不能保证默认比较器对于该函数的性能良好。一开始你认为它是一个列表,所以只要使用
可枚举。Contains
重载并传入
EqualityComparer.Default
@ScottChamberlain:因此,根据经验法则:如果你不知道集合的实际类型以及它使用的比较器,就不要使用
Contains
,否则你会陷入困境。@TimSchmelter我同意这个说法。这是。。。令人不安。几乎和我自己的代码一样难以理解:)
var userRepo = new MobileUserRepository((ILog)null);
var deviceRepo = new MobileDeviceRepository((ILog)null);

IReadOnlyList<MobileUser> allUser = userRepo.GetAllMobileUsersWithDevices();
MobileUser user = allUser.First();

IReadOnlyList<MobileDeviceInfo> allDevices = deviceRepo.GetMobileDeviceInfos(user.VwdId, true);
MobileDeviceInfo device = allDevices.First();
bool contains = user.DeviceInfos.Contains(device);
bool anyEqual = user.DeviceInfos.Any(x => x.DeviceToken == device.DeviceToken);
Assert.IsTrue(contains); // no, it's false
bool contains = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device); // true
 // still false
bool contains2 = ((HashSet<MobileDeviceInfo>)user.DeviceInfos).Contains(device);
// but this is true as already mentioned
bool contains3 = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device); 
System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer
GenericEqualityComparer<T>
bool contains = user.DeviceInfos.Contains(device, EqualityComparer<MobileDeviceInfo>.Default);  // true