【问题标题】:List.Any() returning true when false was expectedList.Any() 在预期为 false 时返回 true
【发布时间】:2016-10-14 17:39:40
【问题描述】:

我们今天在代码中遇到了一个错误。我们有几个列表,其中数据的键是枚举。有多个不同的枚举用作键(下面代码中的 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)));
        }
    }
}

【问题讨论】:

  • 您不能将 Enum 与 Enum 进行比较,因为它们是两种不同的类型。两者在 Enum 中都表示为 0,因此最好将其作为字符串进行比较
  • 您应该重命名您的问题,因为它与枚举的类型可转换性有关。 List.Any 不相关

标签: c# linq


【解决方案1】:

发生这种情况是因为以下返回 true:

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

枚举在内部由一个整数值表示,默认情况下使用的类型is an 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,这就是你得到的结果。

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

【讨论】:

    【解决方案2】:

    我想,每个枚举都转换为它的基础类型——Int32。并且由于您没有为它们中的每一个设置值,因此它们假定为零。

    【讨论】:

      【解决方案3】:

      您正在将 Foo.Bar2 枚举设置为值 B,并且您正在检查 Foo.Bar1 枚举的值 A。 尝试检查正确的枚举。 就像下面的代码:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2010-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-14
          • 2012-11-11
          • 1970-01-01
          • 1970-01-01
          • 2019-03-29
          相关资源
          最近更新 更多