dotnet 中的判等

简介: dotnet 中的判等

前言

前几天,同事在 .net 程序中,遇到一个很 “诡异” 的问题:明明两个值是相等的,可偏偏却不相等,这是怎么回事呢?

初遇问题

刚听到这个问题时,我是满脸的不相信,怎么可能?但是亲自调试一看,确实什么都相等,但确实返回的不相等。见鬼了?为了更近一步研究这个问题,我特意模仿原程序逻辑写了一个简单的测试程序。

示例代码

using System.Collections.Generic;
using System.Linq;

namespace TestOperatorEqualEqual
{
   
   
    class CClassTest
    {
   
   
        public int data1;
        public int data2;
    }

    class Program
    {
   
   
        public static bool IsClassTestEqual(CClassTest lhs, CClassTest rhs)
        {
   
   
            return lhs == rhs;
        }

        static void Main(string[] args)
        {
   
   
            List<CClassTest> testList = new List<CClassTest>();
            for (int idx = 0; idx < 100; ++idx)
            {
   
   
                testList.Add(new CClassTest {
   
    data1 = idx, data2 = idx+1 });
            }

            CClassTest target = new CClassTest{
   
   data1 = 50, data2 = 51 };
            var bContained = testList.Any(item => {
   
    return IsClassTestEqual(item, target); });
            System.Console.WriteLine(bContained ? "yes" : "no");
            System.Console.ReadKey();
        }
    }
}

明明存在相等项(data1== 50, data2 == 51),但是 Any 却返回了 false。是不是很诡异?你能一眼看出上面程序的问题吗?

单纯从 vs 中看源码,通过调试查看,都看不出任何问题。怎么办?一般从源码看不出问题,就需要从更底层来看,对于 C++ 编写的原生程序来说就是查看汇编代码,对于 C# 编写的程序,优先查看 IL(Intermediate Language) 代码。微软提供了查看 IL 代码的工具—— ildasm

ildasm

可以使用 everything 在磁盘上搜索 ildasm 的位置。

search-ildasm-result.png

ildasm 界面中,通过 file -> open 打开需要查看的程序,然后找到需要查看的方法,如下图:

view-ildasm.png

好家伙,直接调用了 ceq,看到这段 IL 代码,我恍然大明白,原来是比较了两个变量的地址!难怪即使两个类的成员一模一样,但是总返回 false 了!

赶紧谷歌一下 .net operator== msdn,发现了官方说明,链接如下:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/equality-operators。读完发现自己读书太少了!下图截取自上面的官方文档。

reference-type-equality-msdn.png

原来,== 对于引用类型来说(class),是比较两个变量是否引用同一个对象,而不是比较两个变量的成员是否相等。

修复

知道问题的本质原因,修改起来就很简单了。只需要按成员比较两个 CClassTest 实例的成员是否相等即可。

关键函数修改如下:

public static bool IsClassTestEqual(CClassTest lhs, CClassTest rhs)
{
   
   
    return lhs.data1 == rhs.data1 && lhs.data2 == rhs.data2;
}

彩蛋

既然文档里说了可以重载 == operator,那么就重载一下呗。这样就不用额外提供一个比较函数了,直接使用 rhs == rhs 这种形式的比较就可以了。于是我大笔一挥,写下了如下代码:

class CClassTest
{
   
   
    public int data1;
    public int data2;

    public static bool operator == (CClassTest lhs, CClassTest rhs)
    {
   
   
        if (lhs == null && rhs == null)
        {
   
   
            return true;
        }
        else if (lhs != null && rhs != null)
        {
   
   
            return lhs.data1 == rhs.data1 && lhs.data2 == rhs.data2;
        }
        else
        {
   
   
            return false;
        }
    }

    public static bool operator != (CClassTest lhs, CClassTest rhs)
    {
   
   
        return !(lhs == rhs);
    }
}

你能看出上面代码的问题吗?

对,上面的写法会导致 stackoverflow

stackoverflow.png

那正确的写法是什么样的呢?

正确的写法如下:

public static bool operator ==(CClassTest lhs, CClassTest rhs)
{
   
   
    if ((object)lhs == null && (object)rhs == null)
    {
   
   
        return true;
    }
    else if ((object)lhs != null && (object)rhs != null)
    {
   
   
        return lhs.data1 == rhs.data1 && lhs.data2 == rhs.data2;
    }
    else
    {
   
   
        return false;
    }
}

说明:高版本的 c# (应该是 c# 7 开始支持的) 还支持使用 is 判断一个变量是否为空。

下面这种写法更优雅! if (lhs is null && rhs is null) ...

源码下载

csdn: https://download.csdn.net/download/xiaoyanilw/18335150

百度云盘链接: https://pan.baidu.com/s/1VmnqoflrbWXFjeQV7TrKTg 提取码: dsqt

总结

  • 每种语言都有自己独特的规则,学习并适应,才能更好的使用它。

  • 多读书!

相关文章
|
6天前
winfrom实现简单计算器V2版本
winfrom实现简单计算器V2版本
18 0
winfrom实现简单计算器V2版本
|
9月前
|
网络协议 编译器 C语言
Visual Studio 2022 中解决使用scanf报错的方法(一劳永逸)
宝子们好呀!在上一篇文章中教大家任何安装完成Visual Studio 2022,还没有安装的朋友们可以到这里来看一下呀:Visual Studio 2022下载安装教程 安装完成后,很多新手小白在使用Visual Studio 2022编译器的过程中使用到scanf后会出现报错的情况,也不知道如果改正,所以今天我就来给大家分享解决这个问题的办法。
261 0
|
6天前
|
搜索推荐 IDE 数据库连接
创建你的第一个Visual Basic程序:步步为营
【4月更文挑战第27天】探索Visual Basic编程,从安装Visual Studio开始,创建首个&quot;HelloWorldApp&quot;。在Form Designer中布局界面,添加Label和Button,设置属性。编写代码实现Button点击显示问候语。运行并调试程序,逐步学习更多控件和VB.NET高级概念,提升编程技能。享受编程旅程,创造无限可能!
|
6天前
|
C++
Visual Studio 2022编译错误 Error MSB8041-此项目需要MFC库解决方案
Visual Studio 2022编译错误 Error MSB8041-此项目需要MFC库解决方案
103 0
|
9月前
怎么删除360base64.dll,这个方法百分百管用
怎么删除360base64.dll,这个方法百分百管用
225 0
|
9月前
|
关系型数据库 MySQL
成功解决:由于找不到 MSVCP100D.dll, 无法继续执行代码。重新安装可能会解决此问题。
成功解决:由于找不到 MSVCP100D.dll, 无法继续执行代码。重新安装可能会解决此问题。
|
11月前
|
编译器 开发工具 Windows
VS2008 未找到编译器可执行文件 csc.exe【当网上其他方法试玩了之后不起作用的时候再用这个方法】
被公司派遣到中国海洋石油惠州炼化公司做项目,做的是生产管理,来了发现他们的项目结构简直烂的要命,和同学们写的毕业设计差不多,然后开发工具用的是vs2008,我电脑是安装了vs2005和vs2010,vs2012就是没有安装vs2008,在安装vs2008的时候那是一番折腾好长时间,然后把vs2008安装好了打开项目代码,生成解决方案发现报了好多错,然后一一解决,最奇怪的是VS2008 未找到编译器可执行文件 csc.exe,我把所以路径都配好了,并且在dos环境下执行csc.exe都可以执行,环境变量路径设置的也么有问题,就是很奇怪重启机子打开项目还是找不到csc.exe
150 0
|
Linux
由于找不到 mfc110u.dll,无法继续执行代码。重新安装程序可能会解决此问题
由于找不到 mfc110u.dll,无法继续执行代码。重新安装程序可能会解决此问题
681 0
由于找不到 mfc110u.dll,无法继续执行代码。重新安装程序可能会解决此问题
“由于找不到MSVCP140.dll,无法继续执行代码,重新安装程序可能会解决此问题等”解决方案
“由于找不到MSVCP140.dll,无法继续执行代码,重新安装程序可能会解决此问题等”解决方案
889 0