List.Any() возвращает true, когда ожидалось false

#c# #linq

#c# #linq

Вопрос:

Сегодня мы столкнулись с ошибкой в нашем коде. У нас есть пара списков, где ключом этих данных является перечисление. Существует несколько разных перечислений, которые используются в качестве ключей (Foo.Bar1 и Foo .Bar2 в приведенном ниже коде).

Все тесты имеют список полей данных, содержащих 1 элемент, где ключ установлен на одно из значений перечисления. Первый и последний тест выполняются, как и ожидалось. Ожидалось, что второй тест завершится успешно, но не удалось. При чтении кода это кажется законным.

Я предполагаю, что при распаковке переменных значения enum преобразуются в целочисленные значения и сравниваются. Что делает их равными, возвращая, таким образом, 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)));
        }
    }
}
 

Комментарии:

1. Вы не можете сравнивать Enum с Enum, потому что это два разных типа. Оба представлены как 0 в Enum, поэтому вам было бы лучше сравнить их как строку

2. Вам следует переименовать свой вопрос, поскольку речь идет о преобразовании типов перечислений. List.Any не связано

Ответ №1:

Это происходит потому, что следующее возвращает true:

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

Перечисления внутренне представлены целым значением, по умолчанию используемый тип — int:

Каждый тип перечисления имеет базовый тип, который может быть любым целым типом, кроме char . Базовый тип элементов перечисления по умолчанию — int .

Этот базовый тип используется при преобразовании одного перечисляемого типа в другой. Итак, для значения enum используется целочисленное значение, а затем на основе этого создается новое значение enum.

По сути, этот процесс работает следующим образом:

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

Если вы не укажете число явно для каждого элемента перечисления, по умолчанию позиция в объявлении перечисления определяет целочисленное значение, начиная с 0 .

Так что в приведенном выше примере integral было бы 0 , поскольку Bar1.A является первым членом Bar1 . И первый член Bar2 is 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:

Я предполагаю, что вы хотите, чтобы и значение enum, и тип были одинаковыми, чтобы 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) amp;amp; (Foo.Bar1)q.Key == Foo.Bar1.A));
    }
 

Если первый предикат имеет значение true, вычисляется второй.