列表.Any()返回true,但预期应该是false。

3
我们今天在代码中遇到了一个错误。我们有一些列表,其中数据的键是枚举类型。有多个不同的枚举类型被用作键(如下面代码中的Foo.Bar1和Foo.Bar2)。
所有测试都有一个DataFields列表,其中包含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)));
        }
    }
}

你不能将枚举类型与另一个枚举类型进行比较,因为它们是两种不同的类型。虽然它们在枚举中都表示为0,但最好将其作为字符串进行比较。 - MistyK
2
你应该重新命名你的问题,因为它是关于枚举类型可转换性的。List.Any与此无关。 - Tim Schmelter
4个回答

8
这是因为以下内容返回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,所以这就是结果。
因此测试失败的原因是在将一个枚举类型转换成另一个类型时,类型标识丢失了。通常,枚举只在它们 自己的 领域内有意义,即当您将一个成员与同一枚举的另一个成员进行比较时。

0

我认为每个枚举都会被转换为其基础类型 - Int32。由于您没有为它们中的每一个设置值,因此它们被视为零。


0
你正在将Foo.Bar2枚举设置为B值,并在检查Foo.Bar1枚举是否为A值。 请尝试检查正确的枚举。 就像下面的代码一样:
new DataField() { Key = Foo.Bar1.B} };

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

0

我猜你想让枚举值和类型都相同,这样 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));
    }

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接