List.Any()在预期为false时返回true

本文关键字:false 返回 true Any List | 更新日期: 2023-09-27 18:13:06

我们今天在代码中遇到了一个bug。我们有几个列表,其中数据的键是枚举。有多个不同的枚举用作键(Foo。Bar1和Foo。下面代码中的Bar2)。

所有测试都有一个包含1项的数据字段列表,其中键被设置为枚举值之一。第一个和最后一个测试按预期运行。第二次测试预计会成功,但失败了。当阅读代码时,它似乎是合法的。

我的假设是,通过拆箱变量,枚举值被转换为整数值,并对它们进行比较。这使得它们相等,从而返回true,从而使Any()方法也返回true。这是正确的吗?还是发生了别的事?

我们应该像第三个测试那样编写比较,使用equals()方法…

如果在下面的单元测试中重新创建了一个非常简化的问题版本。

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)));
        }
    }
}

List.Any()在预期为false时返回true

这是因为下面返回true:

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

枚举在内部由整型值表示,默认情况下使用的类型是int:

每个枚举类型都有一个基础类型,该基础类型可以是除char以外的任何整型。枚举元素的默认基础类型是int。

此基础类型在将一个枚举类型强制转换为另一个枚举类型时使用。因此,对于枚举值,使用整型值,然后在此基础上创建新的枚举值。

基本上,这个过程是这样的:

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

除非在每个枚举成员中显式指定数字,否则默认情况下,位置在枚举声明中决定整数值,从0开始。

所以在上面的例子中,integral将是0,因为Bar1.ABar1的第一个成员。Bar2的第一个成员是Bar2.B,所以这就是你得到的结果。

所以测试失败的原因是因为当将一个枚举转换为另一个类型时,类型标识丢失了。一般来说,枚举只有在其自己的域中才有真正意义,即当您将一个成员与同一枚举的另一个成员进行比较时。

我想,每个enum都被转换为其底层类型Int32。因为你没有给它们每一个设定值它们假设为零。

您正在设置Foo。Bar2枚举到值B,然后检查Foo。Bar1 enum对应值A。尝试检查正确的enum。就像下面的代码:

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

我猜您希望枚举值和类型相同,以便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));
    }

如果第一个谓词为真,则计算第二个谓词。