【问题标题】:FirstOrDefault() result of a struct collection?结构集合的 FirstOrDefault() 结果?
【发布时间】:2013-03-14 10:46:39
【问题描述】:

所以我收集了structs 的集合(它实际上是一个 WCF 数据合同,但我认为这与这里无关)。

List<OptionalExtra> OptionalExtras;

OptionalExtrastruct

public partial struct OptionalExtra

现在我正在运行以下语句:

OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
if (multiOptExtra != null)
{

}

现在这不会编译:

运算符 != 不能应用于 OptionalExtra 类型的操作数 和'&lt;null&gt;'

经过一番谷歌搜索后,我意识到这是因为 OptionalExtrastruct。除非定义为可空类型,否则我认为哪个不可为空?

所以我的问题是,如果我的where 语句没有返回任何结果,那么FirstOrDefault 调用的结果是什么?会不会抛出异常?

顺便说一句,这不应该永远不会发生,但总比抱歉更安全。

【问题讨论】:

    标签: linq c#-4.0 struct


    【解决方案1】:

    如果您的集合为空,FirstOrDefault 将返回 default(OptionalExtras)。结构的默认值是其所有值依次默认初始化的结构(即零、空等)。

    如果您假设会有一个元素并且您的代码不适用于空集合,请改用First(),因为当您的集合为空时会引发异常。快速失败通常比返回错误数据要好。

    如果你不能假设会有一个元素,又不能处理结构体默认初始化,你可以将集合中的结构体设为nullable value type,例如如下:

    OptionalExtras
        .Where(w => w.Code == optExtra.Code)
        .Cast<OptionalExtra?>()
        .FirstOrDefault();
    

    这样,即使是结构,您也可以获得空返回。这里的关键思想是扩展可能值集以包含OptionalExtra 以外的其他内容,以允许检测空列表。如果您不喜欢可空值,您可以改用 Maybe&lt;&gt; 实现(不是 .NET 内置),或使用空列表或单例列表(例如 .Take(1).ToArray()。但是,可空结构可能是您的最佳选择.

    TL;DR;

    • 如果序列为空,.FirstOrDefault&lt;T&gt;() 返回default(T)
    • 如果您认为列表不为空,请改用.First()
    • 当您不能假定列表非空时,转换为可为空,然后使用 .FirstOrDefault&lt;T&gt;()

    【讨论】:

      【解决方案2】:

      正如其他人所说,当没有元素匹配时,您的代码结果将是:

      default( OptionalExtra )
      

      如果您希望返回 null,您可以将列表转换为 OptionalExtra?

      OptionalExtra? multiOptExtra = OptionalExtras.Cast<OptionalExtra?>().Where( ...
      

      然后您可以测试null

      【讨论】:

      • 我无法控制该结构,它是来自第三方的 WCF DataContract。还是谢谢
      • 您不需要控制结构 - 您只是暂时将 List&lt;OptionalExtra&gt; 的元素转换为有效的 LINQ 的 OptionalExtra?
      • 我想我更喜欢@EamonNerbonne 的回答。如果它不存在,我宁愿它失败,而不是花费权力铸造。在这种情况下,失败应该是非常罕见的,因此最好针对最可能的情况进行优化。就像我说的,无论如何,ta
      • +1 当您知道列表是否为非空时,这是要走的路。
      【解决方案3】:

      如果default(OptionExtra) 仍然是有效值,最好将您的代码更改为此

      var results = OptionalExtras.Where(w => w.Code == optExtra.Code).Take(1).ToList();
      if (results.Any()) {
         multiOptExtra = results[0]
      }
      

      【讨论】:

      • 首选ToArray 而不是ToList - 数组更快,使用更少的内存,更严格(即很少有意外错误),并且语法更短。如果您使用 LINQ,几乎没有理由使用 List&lt;&gt;
      • @EamonNerbonne 除非您想将项目添加到结果中。
      • @EamonNerbonne 这似乎是夸大其词(请参阅stackoverflow.com/a/1106012/83171)和微优化的味道。 ToArrayToList 方法之间的性能差异是微不足道的,List&lt;T&gt; 提供了更多功能,使用语法相似并且实际上比数组更安全,因为它们的协方差被破坏,允许像 var x = new string[1]; ((object[])x)[0] = 1; 这样的代码。在这种情况下,当您有许多单元素数组时,这是有道理的,但我不会说“几乎从不将 List 与 LINQ 一起使用”是正确的。
      • 性能差异不在.ToList.ToArray;但在每次后续使用该集合时(以及 GC 对象计数加倍)。其次,如果你正在铸造,你总是会导致异常(但你所说的可能会导致问题,不可否认)。然而,在实践中,List.Add 的存在更可能导致问题,List.Reverse 的存在隐含地替换了 LINQ 的Reverse
      • 我明白你的意思,并且我同意数组的性能更高 - 它们被用作许多其他集合的基础集合。当然List&lt;&gt; 有它的问题。我仍然不同意您的说法,即几乎没有理由将它们与 LINQ 一起使用。作为旁注,我对.ToArray().ToList() 方法进行了微基准测试,在我的笔记本电脑上,.ToArray() 方法似乎触发了更多的垃圾回收。你可以自己试试 - gist.github.com/vas6ili/5164182.
      【解决方案4】:

      结果将是你的结构的默认值,例如default(OptionalExtras).

      而对于引用类型,默认值为null

      【讨论】:

        【解决方案5】:

        它为您的结构提供默认值,如下所示

        int[] numbers = { };
        int first = numbers.FirstOrDefault();
        Console.WriteLine(first);//this print 0 as output 
        

        其他处理选项是使用默认值,如下所示

        List<int> months = new List<int> { };
        // Setting the default value to 1 by using DefaultIfEmpty() in the query. 
        int firstMonth2 = months.DefaultIfEmpty(1).First();
        Console.WriteLine("The value of the firstMonth2 variable is {0}", firstMonth2);
        

        【讨论】:

          【解决方案6】:

          如果要检查 null,请使用 System.Nullable 集合:

          var OptionalExtras = new List<OptionalExtra?>();
          /* Add some values */
          var extras = OptionalExtras.FirstOrDefault(oe => oe.Value.Code == "code");
          if (extras != null)
          {
              Console.WriteLine(extras.Value.Code);
          }
          

          请注意,您必须使用 Value 来访问元素。

          【讨论】:

          • 但结构永远不能为空?
          • 那个?表示法将结构转换为 Nullable 对象,因此是 myStructVar.Value 要求。不确定这样做是否是一个好的设计,但它很简单。
          • 但这并不能回答问题。我问一个结构(值类型)的 FirstOrDefault 的结果是什么。你说如果我使我的结构可以为空(将其更改为引用类型),我将得到一个空值。这不是我要求的。
          • 这不是对您问题的直接回答,但由于您已批准(且正确)答案,我添加了一个案例,您可以安全检查您的 LINQ 查询是否为空。
          【解决方案7】:

          假设 Code 是我回答的目的的字符串,您应该能够测试该值的默认值。

          OptionalExtra multiOptExtra = OptionalExtras.Where(w => w.Code == optExtra.Code).FirstOrDefault();
          if (multiOptExtra.Code != null)
          {
          
          }
          

          【讨论】:

          • 如果Where 返回null multiOptExtra.Code != null 会抛出一个nullReferenceExcepetion,这实际上也不能回答这个问题,这就是firstordefault() 返回一个struct收藏。
          • @Liam:不会返回 Null。我只是采用了您的代码的变体,运行它,FirstOrDefault 返回了一个不为空的空结构。见:harriergroup.com/mll/imagesonweb/struct.png
          • 嗯,实际上你的权利......我忘了这是一个结构。仍然没有回答我的问题。
          猜你喜欢
          • 2021-04-11
          • 1970-01-01
          • 1970-01-01
          • 2011-05-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多