Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List.Any() returning true when false was expected

Tags:

c#

linq

We ran into a bug in our code today. We have a couple of lists where the key of that data is an enum. There are multiple different enums that are used as keys (Foo.Bar1 and Foo.Bar2 in the code below).

All tests have a list of DataFields containg 1 item where the key is set to one of the enum values. The first and last test run as expected. The second test was expected to succeed, but fails. When reading the code it seems legit.

My assumption is that by unboxing the variables, the enum values are converted to there integer values and those are compared. Which make them equal, thus returning true, thus making the Any() method returning true as well. Is this correct? Or is there anything else going on?

We should have written the comparison as in the third test, with an equals() method...

If recreated a very simplified version of the issue in the unit test below.

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)));
        }
    }
}
like image 301
Sorskoot Avatar asked Dec 31 '25 19:12

Sorskoot


1 Answers

This happens because the following returns true:

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

Enums are internally represented by a integral value, by default the type being used is an int:

Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of enumeration elements is int.

This underlying type is used when casting one enum type into another. So for the enum value, the integral value is being used, and then based on that, the new enum value is being created.

Essentially, this process works like this:

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

Unless you specify the number explicitly with each enum member, by default the position in the enum declaration decides the integral value, starting with 0.

So in above example, integral would be 0, since Bar1.A is the first member of Bar1. And the first member of Bar2 is Bar2.B, so that’s what you get as the result.

So the reason the test fails is because when casting one enum into another type, the type identity is lost. In general, enums only make a real sense in their own domain, i.e. when you compare one member with another member of the same enum.

like image 132
poke Avatar answered Jan 03 '26 07:01

poke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!