如何对使用Where过滤掉的项目执行操作?

时间:2018-01-24 15:30:54

标签: c# linq

在我工作的代码库中,有很多这样的代码:

foreach (var item in itemList)
{
    if (someCondition(item))
    {
        Logger.Error($"Oops, {item} is wrong!");
        continue;
    }

    if (someOtherCondition(item))
    {
        Logger.Error($"Bad thing: {item} is wrong!");
        continue;
    }

    // do the stuff
}

使用Linq进行过滤更具可读性,但我还需要一种可读的方法来记录每个项目被过滤掉的时间。 Where扩展程序没有这样的功能。我该怎么用?

4 个答案:

答案 0 :(得分:2)

如果目标是过滤项目(创建没有错误项目的新项目)并一次性执行日志记录,则可以在Where内组合委托(尽管在谓词中执行操作有时是皱起眉头)

var newList = itemList.Where(item =>
{
    string err;
    if(someCondition(item))
        err = $"Oops, {item} is wrong!";
    else if(someOtherCondition(item))
        err = $"Bad thing: {item} is wrong!";
    else
        return true; //no error -> return true to include in list
    Logger.Error(err);
    return false;
});

您也可以使用辅助函数做很多事情。哪个最合适真的取决于一般用例。例如,如果somecondition是一个全局函数,它可以记录错误本身。

如果条件和错误日志是动态生成的,您还可以使用全局帮助函数,例如:

public static bool CheckValid<T>(T value, Func<T,bool> ErrorWhen, Func<T,string> GetError)
{
    if(!ErrorWhen(value))return true;
    Logger.Error(GetError(value));
    return false;
}

然后可以通过以下方式进行链接和过滤:

newList = itemList.Where(item => CheckValid(item , someCondition, i => $"Oops, {i} is wrong!" ))
        .Where(item => CheckValid(item , someOtherCondition, i => $"Bad thing: {i} is wrong!"));

(注意,无论选择哪个选项,只有在实际枚举结果时才会进行过滤和后续记录)

答案 1 :(得分:1)

您必须从Where谓词内部进行日志记录:

foreach (var item in itemList.Where(delegate (ItemType x) { if (someCondition(x)) { Logger.Error("oops, {x} is wrong!"); return false; } return true; })
                             .Where(delegate (ItemType y) { if (someOtherCondition(y)) { Logger.Error($"Bad thing: {y} is wrong!"); return false; } return true; }))
{
    // do stuff
}

虽然我已经展示了使用匿名委托的示例,但您可能更喜欢使用真实的命名方法。这实际上取决于您是否需要捕获变量或条件是静态的。

答案 2 :(得分:1)

虽然你可以将所有逻辑都压缩到LINQ语句中,但它开始看起来很乱并且比你的代码更不易读。我发现使用子程序来执行以下附加任务更加清晰:

var results = itemList.Where(i => CheckSomeCondition(i) && CheckSomeOtherCondition(i));

bool CheckSomeCondition(T item)
{
    if (someCondition(item))
    {
        Logger.Error($"Oops, {item} is wrong!");
        return false;
    }
    return true;
}

bool CheckSomeOtherCondition(T item)
{
    if (someOtherCondition(item))
    {
        Logger.Error($"Bad thing: {item} is wrong!");
        return false;
    }
    return true;
}

答案 3 :(得分:1)

也许我错了,但似乎你的标题与你的内容不太匹配。如果我根据你的问题标题编写代码

  

如何对使用Where?

过滤掉的项目执行操作

似乎最好的方法是过滤并返回结果同时日志过滤掉的项目。封装是非常重要的。否则,您可能会在过滤器和日志记录之间意外添加其他where子句。

假设:

public class Person
{
  public string Name { get; private set; }
  public Person(string name)
  {
    Name = name;
  }
}

public static class IEnumerableExtensions
{
  public static List<T> WhereWithFiltered<T>(this IEnumerable<T> values
    ,Func<T, bool> predicate
    ,Action<IEnumerable<T>> filteredAction)
  {
    var result = values.Where(predicate).ToList();
    var filtered = values.Except(result);
    filteredAction(filtered);
    return result;
  }    
}

public void Log(IEnumerable<T> items)
{
  foreach (var item in items)
  {
    Logger.Error($"Bad thing: {item} is wrong!");
  }
}

示例:

var people = new List<Person>
{
  new Person("Frank"),
  new Person("John"),
  new Person("Joe")
};

var filteredPeople = people
  .WhereWithFiltered(p => p.Name.StartsWith("J"), (filtered) => Log(filtered))
  .WhereWithFiltered(p => Condition1(p), (filtered) => Log(filtered))
  .WhereWithFiltered(p => Condition2(p), (filtered) => Log(filtered));