#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, вычисляется второй.