在 C# 中查询字典的优雅方式

Elegant way to query a dictionary in C#

我正在尝试创建一种优雅且可扩展的方式来查询将枚举映射到一组字符串的字典。

所以我有这个 class SearchFragments 里面有字典。然后我想要一种方法,其中 class 的消费者可以简单地询问 "HasAny" 并且,这是我正在努力的地方,只需传入一些查询,如表达式并得到布尔值答案。

public class SearchFragments
{
    private readonly IDictionary<SearchFragmentEnum, IEnumerable<string>> _fragments;

    public SearchFragments()
    {
        _fragments = new Dictionary<SearchFragmentEnum, IEnumerable<string>>();
    }

    public bool HasAny(IEnumerable<SearchFragmentEnum> of)
    {
        int has = 0;
        _fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));
        return has >= 1;
    }
}

目前这种方式的问题是,这个 class 的消费者现在必须构建一个 IEnumerable<SearchFragmentEnum>,这可能会非常混乱。

我正在寻找的是消费代码将能够按照以下方式编写内容:

searchFragments.HasAny(SearchFragmentEnum.Name, SearchFragmentEnum.PhoneNumber)

但是参数列表的大小可能会有所不同(我不必在 SearchFragments class 中为每个可能的组合编写方法重载(这样,如果将新值添加到 SearchFragmentEnum 在以后的某个日期,我将不必更新 class。

您可以使用params[]

public bool HasAny(params SearchFragmentEnum[] of)
{ ...

旁注:您知道 LIN(Q) 查询应该只查询一个源并且永远不会造成任何副作用吗?但是您的查询确实不必要地增加了整数:

_fragments.ForEach(x => of.ForEach(y => has += x.Key == y ? 1 : 0));

改用这个(效率更高,可读性更高):

return _fragments.Keys.Intersect(of).Any();

一个更有效的替代方案是 想法:

return of?.Any(_fragments.ContainsKey) == true; 

对于 c# 中可变大小的参数,您可以使用 params 关键字: public int HasAny(params SearchFragmentEnum[] of)

出于性能原因,.Net API 通常会为此提供几个重载;传递的参数被复制到一个新数组中。为最常见的情况明确提供重载可以避免这种情况。

public int HasAny(SearchfragmentEnum of1)
public int HasAny(SearchFragmentEnum of1, SearchFragmentEnum of2)
etc.

除了使用参数,您还可以考虑使用 [Flags] 属性标记您的枚举。参数可以像 HasAny(SearchFragmentEnum.Name | SearchFragmentEnum.PhoneNumber 一样传递。 Whosebug 上的例子很多(例如 Using a bitmask in C#

使用 params 关键字允许不同数量的参数。此外,您可以通过遍历较小的 of 数组来简化代码。此外,您正在使用具有 O(1) 键检查的字典,因此不需要内部循环:

public bool HasAny(params SearchFragmentEnum[] of)
{
    foreach(var o in of) {
        if (this._fragments.ContainsKey(o))
            return true;
    }   
    return false;
}

或使用 LINQ 更短

public bool HasAny(params SearchFragmentEnum[] of) {
    return of?.Any(_fragments.ContainsKey) ?? false;
}