谓词查找表?
我有一个 C# 应用程序,其用户界面包含用户可以执行的搜索类型的选项。选项包括“多个术语”(将搜索术语拆分为空格)、“区分大小写”和“正则表达式”。将来可能会添加更多选项。
这些选项存储在属性 IsMultipleTerms、IsCaseSensitive 和 IsRegularExpression 中。
每个选项组合都有不同的搜索谓词,搜索谓词的定义如下:
private bool SearchCaseInsensitive(string field)
{
return field.ToLower().Contains(_searchTermLower);
}
private bool SearchCaseInsensitiveMultiple(string field)
{
return _searchTermsLower.All(field.ToLower().Contains);
}
我像这样过滤列表:
var predicate = GetFilterPredicate();
SearchResults.Where(predicate);
我当前通过使用名为 SearchPredicateOptionSet 的类实现查找:
public class PredicateOptionSet
{
public bool IsCaseSensitive { get; set; }
public bool IsRegularExpression { get; set; }
public bool IsMultipleTerms { get; set; }
public Func<SearchResult, bool> Predicate { get; set; }
public PredicateOptionSet(bool isCaseSensitive, bool isRegularExpression, bool isMultipleTerms,
Func<SearchResult, bool> predicate)
{
IsCaseSensitive = isCaseSensitive;
IsRegularExpression = isRegularExpression;
IsMultipleTerms = isMultipleTerms;
Predicate = predicate;
}
}
我创建一个列表,然后查询它:
private readonly List<PredicateOptionSet> _predicates;
public MainWindow()
{
_predicates = new List<PredicateOptionSet>
{
new PredicateOptionSet(true, false, false, result => Search(result.Name)),
new PredicateOptionSet(false, false, false, result => SearchCaseInsensitive(result.Name)),
new PredicateOptionSet(true, false, true, result => SearchMultiple(result.Name)),
new PredicateOptionSet(false, false, true, result => SearchCaseInsensitiveMultiple(result.Name)),
};
}
private Func<SearchResult, bool> GetFilterPredicate()
{
var predicate = from p in _predicates
where p.IsCaseSensitive == IsCaseSensitive &&
p.IsMultipleTerms == IsMultipleTerms &&
p.IsRegularExpression == IsRegularExpression
select p.Predicate;
return predicate.First();
}
是否存在更清洁的方法来实现这一目标?我觉得我可能错过了一个重要的概念。
I have a C# application with a user interface that contains options for the type of search a user can perform. The options are 'multiple terms' (splits the search term on spaces), 'case sensitive', and 'regular expression'. More options may be added in the future.
The options are stored in the properties IsMultipleTerms, IsCaseSensitive, and IsRegularExpression.
Each combination of options has a different search predicate, and search predicates are defined like so:
private bool SearchCaseInsensitive(string field)
{
return field.ToLower().Contains(_searchTermLower);
}
private bool SearchCaseInsensitiveMultiple(string field)
{
return _searchTermsLower.All(field.ToLower().Contains);
}
I filter the list like so:
var predicate = GetFilterPredicate();
SearchResults.Where(predicate);
I currently achieve the lookup by using a class called SearchPredicateOptionSet:
public class PredicateOptionSet
{
public bool IsCaseSensitive { get; set; }
public bool IsRegularExpression { get; set; }
public bool IsMultipleTerms { get; set; }
public Func<SearchResult, bool> Predicate { get; set; }
public PredicateOptionSet(bool isCaseSensitive, bool isRegularExpression, bool isMultipleTerms,
Func<SearchResult, bool> predicate)
{
IsCaseSensitive = isCaseSensitive;
IsRegularExpression = isRegularExpression;
IsMultipleTerms = isMultipleTerms;
Predicate = predicate;
}
}
I create a list of them and then query it:
private readonly List<PredicateOptionSet> _predicates;
public MainWindow()
{
_predicates = new List<PredicateOptionSet>
{
new PredicateOptionSet(true, false, false, result => Search(result.Name)),
new PredicateOptionSet(false, false, false, result => SearchCaseInsensitive(result.Name)),
new PredicateOptionSet(true, false, true, result => SearchMultiple(result.Name)),
new PredicateOptionSet(false, false, true, result => SearchCaseInsensitiveMultiple(result.Name)),
};
}
private Func<SearchResult, bool> GetFilterPredicate()
{
var predicate = from p in _predicates
where p.IsCaseSensitive == IsCaseSensitive &&
p.IsMultipleTerms == IsMultipleTerms &&
p.IsRegularExpression == IsRegularExpression
select p.Predicate;
return predicate.First();
}
Is there a cleaner way to achieve this? I feel like I may be missing an important concept.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
至少对于检查部分,您可以使用带有
[Flags]
属性的枚举来创建位字段。如果您将来添加更多方法,这可能会更具可扩展性。然后,您可以使用简单的查找表并取消PredicateOptionSet
类。例子:...
....
At least for the check part you could use an Enum with the
[Flags]
attribute to create bit field. That might be a little more extensible if you add more methods in the future. You could then use a simple lookup table and do away with thePredicateOptionSet
class. Example:...
....
也许我看错了,但目前你有两种根本不同的搜索策略:普通搜索策略和正则表达式。两种策略都支持区分大小写或不区分大小写的选项,但这可能是策略的参数。多重匹配问题已经有些特殊,因为无论如何您都必须首先拆分搜索词,然后您已经可以委托回一种简单的搜索策略(将搜索与 AND 或 OR 结合起来)。
为这些方面的每个组合创建单独的 Func 实现感觉有点“矫枉过正”。如果将来会有更多选项,那么确实很容易找到一种“平等”处理这些选项的通用方法,但另一方面,这些选项的行为却截然不同。对于未来的扩展来说,遇到不同实现的组合爆炸也是不利的。
Maybe I see it wrong, but currently you have 2 fundamentally different search strategies: the normal and the regex. Both strategies support an option to be either case sensitive or not, but this could be a parameter of the strategy. The multi match problem is already somewhat special, because you anyway have to split the search term first and then you could already delegate back to one of the simple search strategies (combining the search either with AND or with OR).
Creating a separate Func implementation for each combination of these aspects feels a bit of "overkill". If there will be some more options in the future it is indeed tempting to find a generalized approach that handles these options "equal", but on the other hand these options behave quite differently. It is also on the con side for future extensions that you run into the combinatorial explosion of different implementations.
似乎您可以为每个选项创建一个函数,然后将它们链接起来(通过Where(...) 调用)。这样,您就只有三种方法,而不是四种。此外,如果需要,您还可以通过更多方式组合较小的操作。
It seems that you could make one function per option, then chain them (through Where(...) calls). That way, you only have three methods instead of Four. Also, you can then combine the smaller operation in more ways if you ever need to.