需要将复杂的foreach和IF语句格式化为更好的格式化LINQ表达式

时间:2018-01-03 05:29:11

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

我有一个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表达式。此外,性能明智会更好。请建议。

1 个答案:

答案 0 :(得分:1)

重新:将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)

即使没有唯一的密钥,您也可以在HashCode上使用合适的FieldMappingOption并按键来获得类似的效果,尽管您需要考虑一下在发生哈希冲突时发生。

<强>可读性

外部for循环中的Where谓词可以说是凌乱的,并且可以使用一些重构(为了便于阅读,如果不是为了表现)。

IMO大多数where子句可以作为元属性移动到FieldMappingOption,例如总结

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

进入财产,例如fmo.MustBeValidated等。

你可以通过重新排列&amp;&amp;和&amp; amp;&amp;最有可能首先失败的条款,但如果它影响代码的可读性流,则不会这样做。

相关问题