公司的程序员小高在写代码的时候遇到一个问题,问题也很简单,那就是小高在写一个参数配置的页面的时候,若界面参数发生了改变,那么需要弹窗提醒用户是否保存参数,可这个参数配置页面有几百个参数需要配置,如果要对所有参数与数据库的参数进行逐一比对,那可是个大工程,小高尝试了很多方法,结果都没有达到预期的效果。

打开网易新闻 查看更多图片

小高写的参数配置页面的原本操作逻辑是编辑完页面数据后,点击页面的“返回”按钮,不管页面有没有发生改变,均弹窗提示用户是否保存配置的参数!

当我看到小高的做法以后,我觉得小高开始的做法在用户体验上有点问题,因为用户来这个界面不一定就是要改配置参数,有可能就是看一眼,但每次返回时都要被弹窗询问是否保存数据,从用户角度,这样的软件使用体验肯定是有问题的。

所以,我跟小高说,不要每次都弹窗,而是当配置发生变化时再弹窗!小高觉得我说的也有道理,但是,他随之抛给了我一个问题。

据小高说,他也不是没想过按照我的想法去做,但是,如果一个参数一个参数去比对,那么光验证参数是否改动这块的代码就有成百上千行,这么写不光累,代码可能还不利于维护。

我问他:“用Equals不行吗?”

小高跟我说,Equals他试过,即使两个对象的内容完全相等,使用Equals的比对结果仍然是False!

这让我有点尴尬了,因为我被他这么一说,我才想起来,我似乎从来没有用过Equals!

这得说到我俩的技术背景,都是C#!

在C#中大多数情况下,我们都只会使用“==”号来判断两个对象的值是否相等,之所以不常使用Equals,那是因为“==”和Equals大部分情况下作用是一样的,对于值类型而言(比如1、2、3、4、5),因为值类型都是存储在栈上,因此“==”和Equals判断的都是两个值类型的值是否相等。而对于引用类型而言,使用“==”就是比对两个对象之间的栈地址是否相等,使用Equals就是比对两个对象之间的堆地址是否相等,也就是Equals实际上比对的是两个对象是否是对同一个对象的引用。

因此,假设有一个对象A,此时,如果给B赋值,B=A的情况下,使用Equals来比对,那么A和B是能划上等号的。

再回到开始的需求,小高在参数配置页面点击“返回”时已经将页面上的数据重新new了一个对象,这里我们把这个对象看作B,而每次保存完成以后,需要对原本的参数对象进行重新赋值,这个原本的对象我们看作A,因此B对象(引用类型)的地址和A对象所引用的地址不管对于“==”来说还是Equals来说,其实比较得都是地址而不是内容。因此,这么比的话永远是False。

此时,小高找了一堆第三方库,第三方库其实能够达到比对内容的目的,但是,我把小高的找到的库逐一看了一遍,无奈本人有强迫症,非MIT开源协议的第三方库,我认为都有商业风险,于是,小高找的库全军覆没!

最后,实在没办法,有问题,找百度,我找到了一个比较经济实惠又没有商业风险的做法,那就是将两个需要比对的对象直接序列化成为Json字符串,然后拿两个Json字符串进行比对!

这里要说到C#中的字符串(String)类型的特殊之处了!

众所周知,字符串这个类型在大部分编程语言里,它都是引用类型,在C#里面也是,但是,特殊的地方在于,当两个字符串在进行比对的时候,其实比对的是字符串的值!(大家不用翻论文,总之就是这个意思)

所以,只要两个Json字符串是一模一样的,那么不管使用“==”还是使用Equals,得到的结果均是True,当然了,我还是喜欢使用“==”来比对!

打开网易新闻 查看更多图片

结语

到了这里,小高是算是解决了他所需要解决的问题,但是,我其实还是觉得不够完美,可能最佳的解决方案是通过反射两个对象里面的属性,然后通过比对属性的值来判断两个对象的内容是否相等,但是,这无疑会将问题变得更加复杂,举个很简单的例子,假设一个对象中包含着另一个对象,而另一个对象又包含着另外一个对象……,而每个对象里面的属性的数据类型可能还不一样,需要逐一判断,这无疑增加了代码的复杂性,这种偏底层的东西,我是不愿意碰的!

况且,这也背离了我想要使用简单的方法解决问题的初衷!

但是,我说得不完美,其实还包含了另外一层意思,因为我怕对象转换成Json以后,Json的每一个属性的位置会在最终转换出来的字符串Json中发生变化,这样两个字符串就变得不相等了。

但是,我尝试了很多次,发现无论我怎么转换,最终两个内容完全一样的不同对象,转换出来的Json串都是一模一样的。

所以,您认为将对象转换成Json后进行比对这个方法靠谱吗?有没有其他风险呢?