简体   繁体   English

当假定为false时,List.Any()返回true

[英]List.Any() returning true when false was expected

We ran into a bug in our code today. 我们今天在代码中遇到了一个错误。 We have a couple of lists where the key of that data is an enum. 我们有几个列表,其中数据的关键是枚举。 There are multiple different enums that are used as keys (Foo.Bar1 and Foo.Bar2 in the code below). 有多个不同的枚举用作键(下面的代码中的Foo.Bar1和Foo.Bar2)。

All tests have a list of DataFields containg 1 item where the key is set to one of the enum values. 所有测试都有一个包含1个项目的DataField列表,其中键设置为其中一个枚举值。 The first and last test run as expected. 第一次和最后一次测试按预期运行。 The second test was expected to succeed, but fails. 第二次测试预计会成功,但失败了。 When reading the code it seems legit. 阅读代码时似乎是合法的。

My assumption is that by unboxing the variables, the enum values are converted to there integer values and those are compared. 我的假设是,通过取消装箱变量,将枚举值转换为整数值并进行比较。 Which make them equal, thus returning true, thus making the Any() method returning true as well. 这使它们相等,从而返回true,从而使Any()方法也返回true。 Is this correct? 这个对吗? Or is there anything else going on? 或者还有什么事吗?

We should have written the comparison as in the third test, with an equals() method... 我们应该在第三次测试中用equals()方法编写比较...

If recreated a very simplified version of the issue in the unit test below. 如果在下面的单元测试中重新创建了一个非常简化的问题版本。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;

namespace EnumCastTest
{
    [TestClass]
    public class UnitTest1
    {
        public class DataField
        {
            public Enum Key { get; set; }
        }

        class Foo
        {
            public enum Bar1 { A }
            public enum Bar2 { B }
        }


        [TestMethod]
        public void Field_With_Bar1_A_Should_Return_True()
        {
            List<DataField> fields = new List<DataField> {
                new DataField() { Key = Foo.Bar1.A} };

            Assert.IsTrue(fields.Any(q => (Foo.Bar1)q.Key == Foo.Bar1.A));
        }

        [TestMethod]
        public void Field_Without_Bar1_A_Should_Return_False()
        {
            List<DataField> fields = new List<DataField> {
                new DataField() { Key = Foo.Bar2.B} };

            Assert.IsFalse(fields.Any(q => (Foo.Bar1)q.Key == Foo.Bar1.A));
        }

        [TestMethod]
        public void Field_Without_Bar1_A_Should_Return_False2()
        {
            List<DataField> fields = new List<DataField> {
                new DataField() { Key = Foo.Bar2.B} };

            Assert.IsFalse(fields.Any(q => Foo.Bar1.A.Equals(q.Key)));
        }
    }
}

This happens because the following returns true: 发生这种情况是因为以下内容返回true

var x = Foo.Bar1.A;
var y = (Foo.Bar2)x;
Console.WriteLine(y == Foo.Bar2.B);

Enums are internally represented by a integral value, by default the type being used is an int : 枚举在内部由整数值表示,默认情况下使用的类型是int

Every enumeration type has an underlying type, which can be any integral type except char. 每个枚举类型都有一个基础类型,除了char之外,它可以是任何整数类型。 The default underlying type of enumeration elements is int. 枚举元素的默认基础类型是int。

This underlying type is used when casting one enum type into another. 将一个枚举类型转换为另一个枚举类型时使用此基础类型。 So for the enum value, the integral value is being used, and then based on that, the new enum value is being created. 因此,对于枚举值,正在使用整数值,然后在此基础上创建新的枚举值。

Essentially, this process works like this: 从本质上讲,这个过程是这样的:

var x = Foo.Bar1.A;
int integral = (int)x;
var y = (Foo.Bar2)integral;

Unless you specify the number explicitly with each enum member, by default the position in the enum declaration decides the integral value, starting with 0 . 除非您使用每个枚举成员明确指定数字,否则默认情况下,枚举声明中的位置决定从0开始的整数值。

So in above example, integral would be 0 , since Bar1.A is the first member of Bar1 . 因此,在上面的例子中, integral将是0 ,因为Bar1.A是第一构件Bar1 And the first member of Bar2 is Bar2.B , so that's what you get as the result. Bar2的第一个成员是Bar2.B ,这就是你得到的结果。

So the reason the test fails is because when casting one enum into another type, the type identity is lost. 因此测试失败的原因是因为在将一个枚举转换为另一个类型时,类型标识将丢失。 In general, enums only make a real sense in their own domain, ie when you compare one member with another member of the same enum. 通常,枚举只在自己域中产生真正意义,即当您将一个成员与同一枚举的另一个成员进行比较时。

I suppose, that every enum is converted to its underlying type - Int32. 我想,每个枚举都转换为它的基础类型 - Int32。 and as you don't set values for each of them they assumed as zero. 由于您没有为每个值设置值,因此它们假设为零。

you're setting the Foo.Bar2 enum to the value B and you're checking the Foo.Bar1 enum for the value A. try checking the correct enum. 你将Foo.Bar2枚举设置为值B,并且你正在检查值为A的Foo.Bar1枚举。尝试检查正确的枚举。 like in the below code: 如下面的代码:

new DataField() { Key = Foo.Bar1.B} };

        Assert.IsFalse(fields.Any(q => (Foo.Bar1)q.Key == Foo.Bar1.A));

I guess that you want both the enum value and the type to be the same for the Any(...) to return true. 我想你希望枚举值和类型相同,Any(...)返回true。

    [TestMethod]
    public void Field_Without_Bar1_A_Should_Return_False()
    {
        List<DataField> fields = new List<DataField> {
            new DataField() { Key = Foo.Bar2.B} };

        Assert.IsFalse(fields.Any(q => (q.Key is Bar1) && (Foo.Bar1)q.Key == Foo.Bar1.A));
    }

If the first predicate is true, the second one is evaluated. 如果第一个谓词为真,则评估第二个谓词。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM