【问题标题】:Using List.Find or LINQ on lists of enums in .NET 3.5在 .NET 3.5 中的枚举列表上使用 List.Find 或 LINQ
【发布时间】:2009-11-11 12:38:07
【问题描述】:

当列表不包含我要搜索的元素时,为什么 List.Find(以及列表上的 LINQ 查询)总是返回第一个枚举元素?

场景:

我的枚举:

public enum TestEnum
{
    EnumOne,
    EnumTwo,
    EnumThree
}

我的测试:

var TestEnum1 = TestEnum.EnumOne;
var TestEnum2 = TestEnum.EnumTwo;
var TestEnum3 = TestEnum.EnumThree;

List<TestEnum> testEnumList = new List<TestEnum>();//{ TestEnum1, TestEnum2 };
var selectedWithLinq = (from c in testEnumList where c.Equals(TestEnum3) select c).FirstOrDefault();
var selectedWithListFind = testEnumList.Find(myEnum => TestEnum3.Equals(myEnum)));

在这种情况下 selectedWithLinq 和 selectedWithListFind 都返回 TestEnum.EnumOne。如果我将 TestEnum3 添加到列表中,它将正确返回。

【问题讨论】:

  • List testEnumList = new List();是 TestEnum 类型的空列表,您在哪里填充此列表?它不会自动创建一个包含每个枚举值 1 的列表。
  • 我认为他在评论中是这样做的;)
  • :) 列表是否为空或是否包含任何元素都没有关系。在我搜索不在枚举列表中的元素的任何情况下,List.Find 似乎总是返回枚举定义的第一个元素。

标签: linq list .net-3.5 enums


【解决方案1】:

由于TestEnum 是一个值类型,当在列表中找不到任何元素时,它不能简单地返回null(如果你有一个引用类型数组就会这样做),但它会返回default(TestEnum),它等于在这种情况下,EnumOne。但无论如何,你想达到什么目的?

【讨论】:

  • 作为另一种情况,如果您将枚举中的第一个元素显式设置为 1,那么您仍然会得到 0,但这将是枚举的无效值并且不可测试没有强制转换,给出了null 的良好近似值。不过不要这样做,而是添加一个.None 选项作为第一个选项。
  • @Darin:当元素不在列表中时,我只是对返回元素的这种有点奇怪的行为感到好奇。值类型的默认机制似乎是合乎逻辑的解释。谢谢 :) @Matthew 好主意;如果出现该问题,将牢记这一点!
【解决方案2】:

正如其他人所说,这是由于枚举的默认值与您的第一个枚举值一致。解决此问题的选项:

  1. 使用可空类型:

    var selectedWithLinq = testEnumList.Where(x => x == TestEnum3)
                                       .Select(x => (TestEnum?) x)
                                       .FirstOrDefault();
    if (selectedWithLinq != null)
    {
       var realValue = selectedWithLinq.Value;
        // etc
    }
    
  2. 根据 TryXXX 模式编写自己的扩展方法:

    public static bool TryFirst<T>(this IEnumerable<T> source,
                                   out T found)
    {
        using (IEnumerator<T> iterator = source.GetEnumerator())
        {
            if (iterator.MoveNext())
            {
                found = iterator.Current;
                return true;
            }
            else
            {
                found = default(T);
                return false;
            }
        }
    }
    

【讨论】:

    【解决方案3】:

    因为枚举的第一个成员,除非另有说明,否则为零,这是任何枚举的默认值。

    【讨论】:

      【解决方案4】:

      TestEnum 的默认值将是它的第一项,因此当您执行.FirstOrDefault() 时,如果找不到与您的条件匹配的“第一个”,TestEnum.EnumOne 将作为默认值返回。

      【讨论】:

      • 我认为这不是原因,因为如果没有零枚举值,我希望它不起作用。
      • FirstOrDefault 旨在“工作”,即使它什么也没找到。如果您预计它会失败,请使用 .First() 并捕获异常。
      • 使用“不工作”,我的意思是如果 EnumOne 的值不为零,它将不会返回 EnumOne。您可以通过将 EnumOne 设置为 1 来测试它。在这种情况下,FirstOrDefault 返回 0,它不会映射到 TestEnum 的任何值(但仍然是有效的枚举值)。在问题作者描述的情况下,EnumOne 是(可能的)期望值是巧合。换句话说,FirstOrDefault 在这种情况下不会像 asbjornu 所描述的那样“工作”,因为它不会返回 EnumOne。
      • 根据您的示例(枚举中没有设置明确的值),我的解释是完全正确的。如果您提供了一个显式设置值的示例,其中没有一个值是0,我可以告诉您枚举的默认基本类型是int(您可以通过编写enum TestEnum : byte 来更改基本类型)其默认值实际上是0。 :) 正如 Cylon Cat 所写,如果您希望它在找不到值时失败,您应该使用First()。否则,将返回default(TestEnum)
      【解决方案5】:

      这将有助于您将枚举中的值列表获取到通用列表。无需添加任何静态扩展。

      var _allEnumVals = ((MyEnum[])Enum.GetValues(typeof(MyEnum))).ToList();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-28
        相关资源
        最近更新 更多