当前位置: 首页 > article >正文

windows C#-为类或结构定义值相等性(上)

记录自动实现值相等性。 当你的类型为数据建模并应实现值相等性时,请考虑定义 record 而不是 class。

定义类或结构时,需确定为类型创建值相等性(或等效性)的自定义定义是否有意义。 通常,预期将类型的对象添加到集合时,或者这些对象主要用于存储一组字段或属性时,需实现值相等性。 可以基于类型中所有字段和属性的比较结果来定义值相等性,也可以基于子集进行定义。

在任何一种情况下,类和结构中的实现均应遵循 5 个等效性保证条件(对于以下规则,假设 x、y 和 z 都不为 null):

  • 自反属性:x.Equals(x) 将返回 true。
  • 对称属性:x.Equals(y) 返回与 y.Equals(x) 相同的值。
  • 可传递属性:如果 (x.Equals(y) && y.Equals(z)) 返回 true,则 x.Equals(z) 将返回 true。
  • 只要未修改 x 和 y 引用的对象,x.Equals(y) 的连续调用就将返回相同的值。
  • 任何非 null 值均不等于 null。 然而,当 x 为 null 时,x.Equals(y) 将引发异常。 这会违反规则 1 或 2,具体取决于 Equals 的参数。

 定义的任何结构都已具有其从 Object.Equals(Object) 方法的 System.ValueType 替代中继承的值相等性的默认实现。 此实现使用反射来检查类型中的所有字段和属性。 尽管此实现可生成正确的结果,但与专门为类型编写的自定义实现相比,它的速度相对较慢。

类和结构的值相等性的实现详细信息有所不同。 但是,类和结构都需要相同的基础步骤来实现相等性:

1. 替代虚拟 Object.Equals(Object) 方法。 大多数情况下,bool Equals( object obj ) 实现应只调入作为 System.IEquatable<T> 接口的实现的类型特定 Equals 方法。 (请参阅步骤 2。)

2. 通过提供类型特定的 Equals 方法实现 System.IEquatable<T> 接口。 实际的等效性比较将在此接口中执行。 例如,可能决定通过仅比较类型中的一两个字段来定义相等性。 不会从 Equals 引发异常。 对于与继承相关的类:

  • 此方法应仅检查类中声明的字段。 它应调用 base.Equals 来检查基类中的字段。 (如果类型直接从 Object 中继承,则不会调用 base.Equals,因为 Object.Equals(Object) 的 Object 实现会执行引用相等性检查。)
  • 仅当要比较的变量的运行时类型相同时,才应将两个变量视为相等。 此外,如果变量的运行时和编译时类型不同,请确保使用运行时类型的 Equals 方法的 IEquatable 实现。 确保始终正确比较运行时类型的一种策略是仅在 sealed 类中实现 IEquatable。 有关详细信息,请参阅本文后续部分的类示例。

3. 可选,但建议这样做:重载 == 和 != 运算符。

4. 替代 Object.GetHashCode,以便具有值相等性的两个对象生成相同的哈希代码。

5. 可选:若要支持“大于”或“小于”定义,请为类型实现 IComparable<T> 接口,并同时重载 < 和 > 运算符。

可以使用记录来获取值相等性语义,而不需要任何不必要的样板代码。 

结构示例

下面的示例演示如何在结构(值类型)中实现值相等性:

namespace ValueEqualityStruct
{
    struct TwoDPoint : IEquatable<TwoDPoint>
    {
        public int X { get; private set; }
        public int Y { get; private set; }

        public TwoDPoint(int x, int y)
            : this()
        {
            if (x is (< 1 or > 2000) || y is (< 1 or > 2000))
            {
                throw new ArgumentException("Point must be in range 1 - 2000");
            }
            X = x;
            Y = y;
        }

        public override bool Equals(object? obj) => obj is TwoDPoint other && this.Equals(other);

        public bool Equals(TwoDPoint p) => X == p.X && Y == p.Y;

        public override int GetHashCode() => (X, Y).GetHashCode();

        public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs) => lhs.Equals(rhs);

        public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
    }

    class Program
    {
        static void Main(string[] args)
        {
            TwoDPoint pointA = new TwoDPoint(3, 4);
            TwoDPoint pointB = new TwoDPoint(3, 4);
            int i = 5;

            // True:
            Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
            // True:
            Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
            // True:
            Console.WriteLine("object.Equals(pointA, pointB) = {0}", object.Equals(pointA, pointB));
            // False:
            Console.WriteLine("pointA.Equals(null) = {0}", pointA.Equals(null));
            // False:
            Console.WriteLine("(pointA == null) = {0}", pointA == null);
            // True:
            Console.WriteLine("(pointA != null) = {0}", pointA != null);
            // False:
            Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
            // CS0019:
            // Console.WriteLine("pointA == i = {0}", pointA == i);

            // Compare unboxed to boxed.
            System.Collections.ArrayList list = new System.Collections.ArrayList();
            list.Add(new TwoDPoint(3, 4));
            // True:
            Console.WriteLine("pointA.Equals(list[0]): {0}", pointA.Equals(list[0]));

            // Compare nullable to nullable and to non-nullable.
            TwoDPoint? pointC = null;
            TwoDPoint? pointD = null;
            // False:
            Console.WriteLine("pointA == (pointC = null) = {0}", pointA == pointC);
            // True:
            Console.WriteLine("pointC == pointD = {0}", pointC == pointD);

            TwoDPoint temp = new TwoDPoint(3, 4);
            pointC = temp;
            // True:
            Console.WriteLine("pointA == (pointC = 3,4) = {0}", pointA == pointC);

            pointD = temp;
            // True:
            Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD == pointC);

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }

    /* Output:
        pointA.Equals(pointB) = True
        pointA == pointB = True
        Object.Equals(pointA, pointB) = True
        pointA.Equals(null) = False
        (pointA == null) = False
        (pointA != null) = True
        pointA.Equals(i) = False
        pointE.Equals(list[0]): True
        pointA == (pointC = null) = False
        pointC == pointD = True
        pointA == (pointC = 3,4) = True
        pointD == (pointC = 3,4) = True
    */
}

对于结构,Object.Equals(Object)(System.ValueType 中的替代版本)的默认实现通过使用反射来比较类型中每个字段的值,从而执行值相等性检查。 实施者替代结构中的 Equals 虚方法时,目的是提供更高效的方法来执行值相等性检查,并选择根据结构字段或属性的某个子集来进行比较。

除非结构显式重载了 == 和 != 运算符,否则这些运算符无法对结构进行运算。


http://www.kler.cn/a/419396.html

相关文章:

  • windows 应用 UI 自动化实战
  • 百度 文心一言 vs 阿里 通义千问 哪个好?
  • RealESRGAN技术详解(附代码)
  • 软考高项经验分享:我的备考之路与实战心得
  • Rust学习笔记_07——枚举和范围
  • K8s调度器扩展(scheduler)
  • 网络原理-初识
  • 解密开源大模型如何实现本地化部署并基于本地的知识库进行应用
  • Java基础面试题11:简述System.gc()和Runtime.gc()的作用?
  • 一些面试问题的深入与思考
  • 国际网络安全趋势
  • git push使用
  • 探索Linux的目录结构:深入理解文件系统的组织
  • mongodb配置ssl连接
  • 详解Qt PDF 之 QPdfDocument与 QPdfView 打开与显示pdf
  • 如何在 Debian 7 上设置 Apache 虚拟主机
  • 时频转换 | Matlab基于S变换S-transform一维数据转二维图像方法
  • node == RabbitMQ入门教程
  • 手机控制载货汽车一键启动无钥匙进入广泛应用
  • 综合实验——用户远程登陆并更改文件
  • 网络七层杀伤链
  • 网络安全-夜神模拟器如何通过虚拟机的Burp Suite代理应用程序接口
  • python学习笔记9-零散知识点
  • vue3 路由跳转携带参数以及其他页面接收参数
  • 数据库学习记录03
  • 鸿蒙开发:自定义一个任意位置弹出的Dialog