【问题标题】:Need to format complex foreach and IF statements to better formatted LINQ expression需要格式化复杂的 foreach 和 IF 语句以更好地格式化 LINQ 表达式
【发布时间】:2018-06-12 18:59:00
【问题描述】:

我有一个如下所示的 Foreach 语句

foreach (var fieldMappingOption in collectionHelper.FieldMappingOptions
    .Where(fmo => fmo.IsRequired && !fmo.IsCalculated 
     && !fmo.FieldDefinition.Equals( MMPConstants.FieldDefinitions.FieldValue) 
     && (implicitParents || anyParentMappings 
         || fmo.ContainerType == collectionHelper.SelectedOption.ContainerType))) 
{
    if (!collectionHelper.FieldMappingHelpers
            .Any(fmh => fmh.SelectedOption.Equals(fieldMappingOption))) 
    {
        requiredMissing = true;
        var message = String.Format(
           "The MMP column {0} is required and therefore must be mapped to a {1} column.", 
           fieldMappingOption.Label, session.ImportSource.CollectionLabel);
        session.ErrorMessages.Add(message);
    }
}

我能否将上述复杂的 foreach 和 IF 语句分解为格式更好的 LINQ 表达式。此外,性能方面会更好。请提出建议。

【问题讨论】:

  • 我不太明白你在这里问什么。您的代码中似乎没有什么很复杂。 “格式更好的 LINQ 表达式”是什么意思?您可以通过将String.Format 更改为简单的字符串连接来提高性能。 String.Format 比字符串连接慢。
  • 这可能是 codereview.stackexchange 的更好候选者。
  • 感谢@ZoharPeled 的评论。我想知道是否有任何可能我们可以将 Foreach 更改为 Linq 语句,因为条件太长了

标签: c# entity-framework linq foreach linq-to-sql


【解决方案1】:

Re : 将 Foreach 更改为 Linq 语句

好吧,您可以将两个 for 循环转换为 LINQ Select,并且由于在循环内部,您只有一个带有附加谓词的分支,您可以将谓词组合到外部循环中,如下所示:

var missingFieldMappingOptions = collectionHelper.FieldMappingOptions
  .Where(fmo => fmo.IsRequired && !fmo.IsCalculated 
     && !fmo.FieldDefinition.Equals( MMPConstants.FieldDefinitions.FieldValue) 
     && (implicitParents || anyParentMappings 
         || fmo.ContainerType == collectionHelper.SelectedOption.ContainerType))
     && !collectionHelper.FieldMappingHelpers
        .Any(fmh => fmh.SelectedOption.Equals(fmo)))
  .Select(fmo => 
      $"The MMP column {fmo.Label} is required and therefore" +
      $" must be mapped to a {session.ImportSource.CollectionLabel} column."); 

var requiredMissing = missingFieldMappingOptions.Any();
session.ErrorMessages.AddRange(missingFieldMappingOptions)

但是,即使 LINQ 也无法使 .Where 中的过滤子句消失,因此 LINQ Select 几乎不比 for 循环更具可读性,并且实际上也没有更高的性能(可能会有一些边际收益设置requiredMissing 标志并将session.ErrorMessages 添加到一个大块中。

性能

从性能的角度来看,下面是有问题的,因为它在外部for循环中组合时会是O(N log N)(幸运的是,如果找到匹配项,.Any()会提前返回,否则会像N^2一样糟糕):

if (!collectionHelper
    .FieldMappingHelpers.Any(fmh => fmh.SelectedOption.Equals(fieldMappingOption)))

FieldMappingOption 有唯一的密钥吗?如果是这样,那么建议将Dictionary<Key, FieldMappingOption> 添加到collectionHelper 然后使用.ContainsKey(key) 其中approaches O(1),例如

!collectionHelper
    .SelectedFieldMappingOptions.ContainsKey(fieldMappingOption.SomeKey)

即使没有唯一的密钥,您也可以在 FieldMappingOption 上使用像样的 HashCode 并通过该密钥获得类似的效果,尽管您需要考虑在发生哈希时会发生什么碰撞。

可读性

外部 for 循环中的 Where 谓词可以说是混乱的,可能需要进行一些重构(为了可读性,如果不是为了性能)。

IMO 大多数 where 子句可以作为元属性移入 FieldMappingOption,例如结束了

fmo.IsRequired 
&& !fmo.IsCalculated 
&& !fmo.FieldDefinition.Equals(MMPConstants.FieldDefinitions.FieldValue) 

进入一个属性,例如fmo.MustBeValidated等

您可以通过确保谓词尽快返回 false 来压缩较小的性能,方法是重新安排最有可能首先失败的 && 子句,但如果这会影响代码的可读性流程,则不会这样做。

【讨论】:

  • 恕我直言,fmo.MustBeValidated 的建议是这篇文章最好的地方。为此+1。但是,除非存在真正的、可衡量的性能问题,否则这就是过早的优化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
相关资源
最近更新 更多