在我工作的代码库中,有很多这样的代码:
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
扩展程序没有这样的功能。我该怎么用?
答案 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));