【问题标题】:C# LINQ query on anonymous types containing dynamic listC# LINQ 查询包含动态列表的匿名类型
【发布时间】:2019-06-25 21:08:44
【问题描述】:

我有一段 C# 代码,其中我创建了一个 匿名类型 对象,如下所示:

    var measurementUnits = new List<dynamic>() {
    new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
    new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
    new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
    new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
};

通过 LINQ,我想访问 Ratio,其中 Display="HEC"(不区分大小写)类似于:

var multiplier = measurementUnits.Where(m => m.Display == "HEC").First().Ratio;

【问题讨论】:

  • 你有什么理由在这里使用dynamic吗?我没有看到你正在做的任何动态......为什么不创建一个匿名类型的列表,并保留静态类型的所有好处?
  • 顺便说一句,“H”或“HEC”。您的代码和描述不完全匹配

标签: c# list linq dynamic anonymous-types


【解决方案1】:

一种可能的解决方案是避免使用dynamic 关键字。试试这个代码

var measurementUnits = new [] {
    new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
    new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
    new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
    new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
};

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).First().Ratio;

另外,最好将First 替换为FirstOrDeafult 并手动测试它是否为null,以避免出现NullReference 异常

var unit = measurementUnits.FirstOrDefault(m => m.Display.Contains("HEC"));
if (unit != null)
    var multiplier = unit.Ratio();    

【讨论】:

  • 只有在不包含这样的显示并且正确处理这种情况的情况下避免异常的答案。但是您可以将Where 替换为FirstOrDefault(..) 以使其更加简洁。
  • @Rango 谢谢。我相信我们已经有足够多的“什么是 NullReference?”的副本。 :)
  • 如果我需要像Contains("HeC") 这样不区分大小写的搜索怎么办?
  • @Sujoy:通过StringComparer.CurrentCultureIgnoreCase,那么你不再使用List.Contains,而是使用LINQ的扩展方法Enumerable.Contains
  • @Rango 你错了,我收到错误No overload for method 'Contains' takes '2' arguments'
【解决方案2】:

包含将是您的最佳选择,但静态类型会更好,而不是使用动态类型。

What is the difference between statically typed and dynamically typed languages?

var measurementUnits = new List<dynamic>() {
     new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5  ,
     new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5 },
     new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
     new { Unit = "SQM", Display = new List<string>() { "M", "SQ M"}, Ratio=4.5 } 
};

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).First().Ratio;

您还需要检查 null 的乘数,因为您使用 First 如果没有找到值,这将引发空引用异常,更好的解决方案:

var multiplier = measurementUnits.Where(m => m.Display.Contains("HEC")).FirstOrDefault()?.Ratio;

【讨论】:

    【解决方案3】:

    从技术上讲,你可以这样写(请注意,Display 是一个集合,可以Contain 但不等于单个项目"H"):

      // 2.5
      var multiplier = measurementUnits
        .First(item => item.Display.Contains("H"))
        .Ratio;
    

    但是,我建议为此使用 自定义类,而不是容易出现运行时错误的 dynamic(如果 Displaystring,而不是 List&lt;string&gt;)。

    编辑:如果有可能没有这样的项目(例如,"HEC2")并且您不希望 exception 被抛出而是一个默认值,将First 更改为FirstOrDefault

      // 0.0 - default ratio, since "HEC2" is not found
      var multiplier = measurementUnits
        .FirstOrDefault(item => item.Display.Contains("HEC2"))   
       ?.Ratio ?? 0 /* Default Ratio Value Here */;
    

    【讨论】:

    • 如果不包含此类显示,则会出现异常
    • @Rango:是的,我认为这是一种期望的行为(因为First() 在 OP 的原始代码中)
    • 还有Where(m=&gt;m.Display=="HEC"),所以这只是伪代码。我不会从那里得出任何结论。正如 Aleks 在另一条评论中所说:“我相信我们已经有足够多的重复什么是 NullReferenceException,我该如何解决它?
    【解决方案4】:

    我相信您想列出 Display“具有”“HEC”的位置,因为它是一个列表。 PS:在您的 OP 中不清楚您是要按“H”还是“HEC”过滤(代码和描述不匹配)。假设它是“HEC”

    var result = measurementUnits.Where(x=>x.Display.Contains("HEC")).Select(x=>x.Ratio);
    

    或 如果你想要第一个

    var result = measurementUnits.Where(x=>x.Display.Contains("HEC")).First().Ratio;
    

    【讨论】:

      【解决方案5】:

      这是解决方案:

      var measurementUnits = new [] {
          new { Unit = "SQF", Display = new List<string>() { "F", "FT", "SQ FT" }, Ratio=1.5 } ,
          new { Unit = "Hectares", Display = new List<string>() { "H", "HEC"} , Ratio=2.5},
          new { Unit = "Acres", Display = new List<string>() { "AC(TO)" } , Ratio=3.5},
          new { Unit = "SQM", Display = new List<string>() {  "M", "SQ M"}, Ratio=4.5 }
      };
      var multiplier = measurementUnits.Where(m => m.Display.IndexOf("H") > -1).FirstOrDefault()?.Ratio;
      

      【讨论】:

        【解决方案6】:

        你可以用 FirstOrDefault 做到这一切:

         var multiplier = measurementUnits.FirstOrDefault(x => x.Display.Contains("H"))?.Ratio
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-19
          • 2016-06-22
          • 1970-01-01
          • 2013-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多