看似相同的基元值并不相等

Seemingly identical primitive values aren't equal

提问人:MBender 提问时间:2/7/2018 最后编辑:MBender 更新时间:2/7/2018 访问量:113

问:

一个奇怪的案例突然出现。两个对象的两个属性被强制转换为相同的基元类型,并且(似乎)具有相同的值。但是,相等比较器返回 。如果我们使用该方法(或其他比较两个值的方法),那么我们会得到正确的结果。falseEquals

更奇怪的是,实际上将强制转换的结果放入一个新变量中似乎也有效。

下面是一个非常简化的代码示例,在复制和编译时不会产生相同的结果。它只是用于说明发生问题的一般设置。

class Program
{
    static void Main(string[] args)
    {
        var v1 = new Object1 { SomeValue = (short)-1d };

        var invalidResult = (int)v1.SomeValue == (int)SomeEnum.Value1;     //for some reason this returns false
        var validResult = ((int)v1.SomeValue).CompareTo((int)SomeEnum.Value1) == 0;    //this works
        var extraValidResult = ((int)v1.SomeValue).Equals((int)SomeEnum.Value1);
        var cast1 = (int)v1.SomeValue;
        var cast2 = (int)SomeEnum.Value1;
        var otherValidResult = cast1 == cast2;                          //this also works
    }
}

public class Object1
{
    public short SomeValue { get; set; }
}

public enum SomeEnum : short
{
    Value1 = -1,
    Value2 = 0,
    Value3 = 1
}

下面是 VS 监视窗口的屏幕截图,作为我们所看到的内容的证明:

Proof

我知道有时 VS 可以在“监视”窗口中显示无效值,但效果不仅限于该窗口,而且在我们的一个测试中,案例实际上未能通过检查,而它不应该检查。AFAIK 代码中没有诡计(如覆盖或 )。if==Equals

这里可能发生了什么?

(我们显然已经使用该方法“修复”了问题,但我们仍然在挠头,想知道到底发生了什么......CompareTo

编辑:
我意识到上面的代码示例是......有点没用。但是,发布有问题的实际代码可能被证明是非常困难的;有很多。最后,某些对象的“实时”值是从 SQL Server(使用实体框架)填充的,这会使代码共享更加复杂。我很乐意尝试回答任何其他问题以尝试缩小问题范围,但不幸的是,共享完整代码是不可能的(它的特定块是可能的,但由于显而易见的原因它不会编译)。提供了示例代码以显示该问题有多奇怪。

编辑2:
很抱歉耽搁了。以下是有问题的特定方法:

public bool IsLocalizationBlockedByMagPElem(int localizationId)
{
    IEnumerable<MagPElem> magPElems = MagPElemRepository.GetByLocalizationIdAndStatusesOrderedByIdDescThenSubLpAsc(localizationId, DocumentStatus.InBuffer, DocumentStatus.InBufferReedition);

    if (magPElems.Count() != 0)
    {
        var magPElem = magPElems.First();
        //this commented out code did not return the expected value due to the strange comparison issue
        //return (magPElem.MaP_GIDTyp == (int)ExternalSystemType.PM_GIDTyp || (magPElem.MaP_GIDTyp == (int)ExternalSystemType.MP_GIDTyp && magPElem.MaP_SubGIDLp == (short)LocalizationDirection.Destination));
        //to avoid the issue CompareTo is being used, but Equals would work just as well
        return (magPElem.MaP_GIDTyp == (int)ExternalSystemType.PM_GIDTyp || (magPElem.MaP_GIDTyp == (int)ExternalSystemType.MP_GIDTyp && magPElem.MaP_SubGIDLp.CompareTo((short)LocalizationDirection.Destination) == 0));
    }
    return false;
}

我还更新了校样屏幕截图,以包含更多屏幕。在屏幕截图中,与上述代码存在一些细微的差异,因为我们正在测试以查看发生了什么(例如尝试不同的强制转换或将强制转换结果分配给变量)。但问题的要点就在那里。

C# 强制转换 相等 基元

评论

1赞 NDJ 2/7/2018
invalidResult 对我来说是真的......Visual Studio 2015,目标框架 4.5.2(预计到达时间:缺少代码截取不会产生相同结果的注释)
2赞 Marc Gravell 2/7/2018
“并且在复制和编译时不会产生相同的结果”——那么区别就在于你发布的内容和你的真实代码之间的差异。这样就缩小了范围。但。。。如果没有重现错误的实际代码,这将很难回答。看起来它应该可以工作,我会给你 - 但是:VS 监视窗口作为这些东西的预言机不是很可靠。但是,是的,它是 -1 作为 int,-1 作为 int 和 false: 这一事实非常奇怪
2赞 Evk 2/7/2018
最好发布您的真实代码,因为简化的设置无论如何都是无用的。
1赞 Marc Gravell 2/7/2018
随机思考:在实际代码中,这些属性中的任何一个都有副作用吗?即是否一遍又一遍地可靠地返回 -1 值?同样?如果这些属性有副作用:所有的赌注都关闭了......LocalizationDirection.DestinationmapPElem.MaP_SubGIDLp
2赞 Sean Reid 2/7/2018
Visual Studio 2017 .net 4.6.2 和分配给 invalidResult 的值为 true,但表达式在监视窗口中显示为 false。(int)v1.SomeValue == (int)SomeEnum.Value1

答: 暂无答案