值 规则 ID CA1066 类别 设计 修复是中断修复还是非中断修复 非中断 原因 值类型(结构)重写 Equals 方法,但不实现 IEquatable<T>。 请考虑实现 IEquatable<T> 接口以支持强类型相等性测试。 System.IEquatable<T>.Equals 实现应返回与 Equals 一致的结果。 如何解决冲突 若要解决冲突,请实现 IEquatable<T> 并更新 Equals 重写,以调用此实现的方法。 相关规则 CA1067:实现 IEquatable 时重写 Equals 另请参阅 设计规则
这节来讲一下泛型接口:IEquatable。 IEquatable泛型接口处于System.Runtime命名空间下,最早在.NET Framework 2.0中发布,只有泛型版本。 IEquatable中有什么? 我们先看一下IEquatable泛型接口中的元素: 这个接口里边只有一个方法:Equals,返回一个Bool值,从名称中我们可以得知,这个接口规定了一个用于和其它类型作比较的规范,所有实现了这个接口的类 但是这个方法接受一个Object类型的参数,存在装箱和类型安全这样的问题,因此我们也有可能需要一个性能高一些的,并且类型安全的比较方法,为了规范代码,微软推出了IEquatable泛型接口。 我们可以认为Object的Equals是IEquatable的非泛型版本方法,但是在实际应用过程中,我还是推荐大家显式实现IEquatable,有一个良好的代码规范,会让人舒心,关于显示实现接口相关介绍请查看
值 规则 ID CA1066 类别 设计 修复是中断修复还是非中断修复 非中断 原因 值类型(结构)重写 Equals 方法,但不实现 IEquatable<T>。 请考虑实现 IEquatable<T> 接口以支持强类型相等性测试。 System.IEquatable<T>.Equals 实现应返回与 Equals 一致的结果。 如何解决冲突 若要解决冲突,请实现 IEquatable<T> 并更新 Equals 重写,以调用此实现的方法。 相关规则 CA1067:实现 IEquatable 时重写 Equals 另请参阅 设计规则
值 规则 ID CA1067 类别 设计 修复是中断修复还是非中断修复 非中断 原因 类型实现 IEquatable<T>,但不实现 Equals。 规则说明 实现 IEquatable<T> 接口的类型指示它可支持对类型的两个实例进行比较以确定二者是否相等。 Equals 实现应返回与 System.IEquatable<T>.Equals 实现一致的结果。 如何解决冲突 若要解决冲突,请调用 Equals 实现来重写 System.IEquatable<T>.Equals 并实现它。 相关规则 CA1066:重写 Equals 时实现 IEquatable 另请参阅 设计规则
值 规则 ID CA1067 类别 设计 修复是中断修复还是非中断修复 非中断 原因 类型实现 IEquatable<T>,但不实现 Equals。 规则说明 实现 IEquatable<T> 接口的类型指示它可支持对类型的两个实例进行比较以确定二者是否相等。 Equals 实现应返回与 System.IEquatable<T>.Equals 实现一致的结果。 如何解决冲突 若要解决冲突,请调用 Equals 实现来重写 System.IEquatable<T>.Equals 并实现它。 相关规则 CA1066:重写 Equals 时实现 IEquatable 另请参阅 设计规则
实现步骤 重写object.Equals()方法 实现IEquatable<T>.Equals()接口方法 重写 == 和 ! 而实现IEquatable<T>.Equals()接口方法,可以避免装箱,并且保证类型安全。 而实现==和!=,也就允许值类型使用该操作符了,写起来更方便直观,易于理解。 实现IEquatable<T>接口 首先来实现IEquatable<T>接口。 但是这个方法仍然涉及到装箱操作,所以还是IEquatable<T>的实现方法更快一些。 总结 在这几个动作里,实际的逻辑写在了IEquatable<T>.Equals()方法里,object.Equals()就是检查类型然后调用IEquatable<T>.Equals(),== 和 !
public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> { 补上 IEquatable 接口 有了这个思路,我也跟FCL学,让Point实现 IEquatable<T>接口,然后在TProxy<T>代理类中约束下必须实现IEquatable<T>,修改代码如下: public struct Point : IEquatable<Point> { ... } public class TProxy<T> where T: IEquatable< 因为这里的runtimeType实现了IEquatable<T>接口,所以代码返回了一个泛型比较器:GenericEqualityComparer<T>,然后我们继续查看这个泛型比较器是咋样的。 ? 从图中可以看到最终还是对T进行了IEquatable<T>约束,不过这里给提取出来了,还是挺厉害的,然后我也学的模仿一下: ? 可以看到也走了我的自定义实现,两种方式大家都可以用哈。
IReadOnlyRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> where TKey : IEquatable TKey> : IReadOnlyRepository<TEntity, TKey> where TEntity : class, IEntity<TKey> where TKey : IEquatable >(IUnitOfWork unitOfWork) where TEntity : class, IEntity<TKey> where TKey : IEquatable >(IUnitOfWork unitOfWork) where TEntity : class, IEntity<TKey> where TKey : IEquatable >(IUnitOfWork unitOfWork) where TEntity : class, IEntity<TKey> where TKey : IEquatable
IEquatable<T> System.Object的static bool Equals(object obj)这个方法,因为其参数是object类型,所以它可以对任何引用类型进行比较。 而IEquatable<T>这个接口就可以解决这些问题。 它只定义了一个方法:bool Equals(T other)。 例子,三个int: ? 使用它的Equals()方法: ? 所有的原始类型都实现了IEquatable<T>接口。int, byte... 而IEquatable<T>对值类型非常有用。 但是对引用类型没有太大的用处,因为引用类型比较时不存在装箱问题,而且IEquatable<T>在继承方面还是存在问题的,但是string还是实现了IEquatable<T>接口,因为string是seal 需要注意的是如果实现了IEquatable<T>,那么它的实现方法和重写的object.Equals()方法应该保持一致,做同样的事。
interface IEquatable<T> { bool Equals(T obj); } 继承IEquatable的类必须实现Equals的方法,IEquatable
✓ 需要确定数据的状态,定义为 0、false、null 是正确的 防止开发者不从构造函数进行赋值 ✓ 建议结构体继承 System.IEquatable 因为默认的比较使用的是引用比较,而结构体在使用经常会被复制 而且重写 IEquatable 可以减少装箱和拆箱。
✓ 需要确定数据的状态,定义为 0、false、null 是正确的 防止开发者不从构造函数进行赋值 ✓ 建议结构体继承 System.IEquatable 因为默认的比较使用的是引用比较,而结构体在使用经常会被复制 而且重写 IEquatable 可以减少装箱和拆箱。
在之前重写值类型相等性的文章里,我还为值类型实现了IEquatable<T>接口,而对于引用类型来说,就没有必要去实现该接口了,可以把相等性判断逻辑放在object.Equals()方法里。 为什么不实现IEquatable<T> 如果我在Citizen类里面实现了该接口: ? 那么方法里的调用也还是调用virtual的Equals(),否则的话还是一样的bug。 所以针对引用类型,不建议实现IEquatable<T>接口。 非得实现的话建议sealed 例如: ? 为sealed的class实现IEquatable<T>接口肯定是可行的,但是否值得呢? 优点:能得到微小的性能提升,string就是个例子。 综上个人建议是针对引用类型不去实现IEquatable<T>接口。
✓ 需要确定数据的状态,定义为 0、false、null 是正确的 防止开发者不从构造函数进行赋值 ✓ 建议结构体继承 System.IEquatable 因为默认的比较使用的是引用比较,而结构体在使用经常会被复制 而且重写 IEquatable 可以减少装箱和拆箱。
有如下代码实例: SegmentType1.cs: //接口约束:IEquatable<string>; //where TFirst:class :和类型参数约束 partial class SegmentType<TFirst,TSecond>:IEquatable<string> where TFirst:class { //实现IEquatable public void Dispose() { } } 以上的接口和基类约束中,也可以使用如下方法: SegmentType1.cs: //接口约束:IEquatable <string>; //where TFirst:class :和类型参数约束 partial class SegmentType<TFirst,TSecond>:IEquatable< IDisposable partial class SegmentType<TFirst, TSecond> :EventArgs,IDisposable { //实现IEquatable
IComparable/IEquatable 1) 要为值类型实现IEquatable<T> 值类型的Object.Equals方法会导致装箱操作,而且默认实现使用了反射,所以效率不高。 IEquatable<T>可提供好得多的性能。 2) 要在实现IEquatable<T>.Equals时,同样遵循为覆盖Object.Equals而制定的规范 参见: Object.Equals 3) 要在实现IEquatable<T>的同时覆盖Object.Equals 4) 考虑在实现IEquatable<T>的同时重载operator == 和operator ! 考虑在覆盖Object.Equals方法的同时实现IEquatable<T>接口 D.不要从Equals方法中抛出异常 E.
u.ToString()); } } Console.Read(); } } class User:IEquatable <User>//继承IEquatable接口,实现Equals方法。
GetHashCode的高效定义方法 HashCode.Combine class Person:IEquatable<Person> { private int Age { get HashCode.ToHashCode class Person:IEquatable<Person> { public int Age { get; set; } class Person:IEquatable<Person> { public int Age { get; set; } public string Name
下面这种试下 List<User> nonDuplicateList1 = users.Distinct().ToList();//通过User类的Equals实现去重 class User:IEquatable <User>//继承IEquatable接口,实现Equals方法。
要转换的目标类型也必须是其基类,上述例子则是Foo隐式转为FooBase 逆变(Contravariance) 内置的泛型逆变委托Action、Func 、Predicate,内置的泛型逆变接口IComparable<T>、IEquatable other); } public interface IEquatable<T> { bool Equals(T?