【问题标题】:Lambda expression to return zero if null如果为空,则 Lambda 表达式返回零
【发布时间】:2010-12-28 11:29:56
【问题描述】:

在我的 WPF 应用程序中,我曾经将控件动态添加到 Canvas。控件的名称格式为“Control_UniqueValue”。

即,如果我将第一个控件添加到画布,则名称将是“Control_1”,下一个将是“Control_2”等...

我的要求是获得添加控件的最大值

我为此使用了以下语句

string maxId = (string)canvas1.Children.Cast<FrameworkElement>().ToList().Max(x => (x.Name.Substring(x.Name.LastIndexOf('_') + 1)));

但这里的问题是

  1. 需要将值作为int返回

  2. 如果画布不包含任何控件,则会引发错误(尝试使用Nullable 类型,但失败)

【问题讨论】:

    标签: c# lambda linq-to-objects


    【解决方案1】:
    int  maxId = canvas1.Children
        .Cast<FrameworkElement>()
        .Select(e => int.Parse(e.Name.Substring(e.Name.LastIndexOf('_'))))
        .DefaultIfEmpty()
        .Max();
    

    如果序列中没有元素,这应该返回 0 而不是抛出异常。此外,不需要在您的代码中调用 ToList。如果任何控件名称不是预期的格式,这仍然会引发异常。

    【讨论】:

      【解决方案2】:

      要获取整数,您可以使用int.Parse 方法。您不必致电ToList()

      没有错误检查,因此您的控件必须根据您的规则命名name_id

      如果序列为空,可以使用DefaultIfEmpty扩展方法提供默认值:

      int maxId = canvas1.Children
                   .Cast<FrameworkElement>()
                   .DefaultIfEmpty(new FrameworkElement() { Name = "Control_0" })
                   .Max(x => (int.Parse(x.Name.Split('_')[1]) + 1)));
      

      【讨论】:

      • 无法从用法中推断方法“System.Linq.Enumerable.DefaultIfEmpty(System.Collections.Generic.IEnumerable, TSource)”的类型参数。尝试明确指定类型参数。
      • 我已经编辑了我的答案。您必须提供一个 FrameworkElement 类的实例作为空原因列表的默认值。
      【解决方案3】:

      由于 Max() 在其序列中至少需要一个元素,因此您要么必须

      • 捕获异常并将最大值设置为零
      • 在调用 Max() 之前检查长度。 我会推荐这个

      虽然可以在一个长查询表达式中编写逻辑,但我建议您将其拆分一下以提高可读性。创建一个返回子控件名称的整数部分的方法:

      private int NumberFromElementName(string name)
      {
          // Or search for the last '_' using name.LastIndexOf()
          var numString = name.Substring("Control_".Length);
          return Int32.Parse(numString);
      }
      

      然后分两步进行查询,以便查看返回序列的长度。请注意,我将其转换为数组以避免必须运行两次查询。我还使用了OfType 扩展方法,这样即使画布有一个不属于FrameworkElement 类型的子对象,它也能正常工作。

      var children = canvas1.Children.OfType<FrameworkElement>().ToArray();
      int max = 0;
      if (children.Length > 0)
          max = children.Max(x => NumberFromElementName(x.Name));
      string nextChildName = String.Format ("Control_{0}", max + 1);
      

      编辑:正如@Jan 在他的回答中指出的那样,您可以通过在查询中的 Max 调用之前使用 DefaultIfEmpty 来避免将查询分成两部分。

      【讨论】:

        【解决方案4】:

        得到最终答案

        int maxId = canvas1.Children .Cast<FrameworkElement>() .Select(e => int.Parse(e.Name.Substring(e.Name.LastIndexOf('_')+ 1))) .DefaultIfEmpty() .Max(x => x + 1); 
        

        感谢简和李

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-19
          • 1970-01-01
          • 1970-01-01
          • 2017-11-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多