嵌套切换语句:架构设计问题

Ari*_*edi 9 c#

我正在开发一个项目,我的任务是添加一个高级搜索和过滤选项,允许用户通过指定任意数量的条件来查询Windows事件列表中的所需结果.

我们的想法是每个Windows事件日志中有一些特性,如LogName,Source,CreatedDate,Message,Number,等(的一部分FieldItem枚举).总共有4种 possbile数据类型:String,DateTime,Integral (Int/Long),和EventEntryType.这四种数据类型中的每一种都有自己的选择器操作数集合(SelectorOperator枚举的一部分).这是一张图片,可以让您更好地了解整体结构如何:

我最初实现这个想法是这样的:

 public static class SearchProvider
{
    public static List<EventLogItem> SearchInLogs(List<EventLogItem> currentLogs, SearchQuery query)
    {
        switch (query.JoinType)
        {
            case ConditionJoinType.All:
                return SearchAll(currentLogs, query);
            case ConditionJoinType.Any:
                return SearchAny(currentLogs, query);
            default:
                return null;
        }
    }

    private static List<EventLogItem> SearchAll(List<EventLogItem> currentLogs, SearchQuery query)
    {
        foreach (SearchCondition condition in query.Conditions)
        {
            switch (condition.FieldName)
            {
                case FieldItem.Category:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Contains:
                            currentLogs = currentLogs.Where(item => item.Category.ToLower().Contains(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.EndsWith:
                            currentLogs = currentLogs.Where(item => item.Category.ToLower().EndsWith(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.Is:
                            currentLogs = currentLogs.Where(item => string.Equals(item.Category, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
                            break;
                        case SelectorOperator.StartsWith:
                            currentLogs = currentLogs.Where(item => item.Category.ToLower().StartsWith(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.InstanceID:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Equals:
                            currentLogs = currentLogs.Where(item => item.InstanceID == long.Parse(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.IsGreaterThan:
                            currentLogs = currentLogs.Where(item => item.InstanceID > long.Parse(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.IsLessThan:
                            currentLogs = currentLogs.Where(item => item.InstanceID < long.Parse(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.LogName:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Contains:
                            currentLogs = currentLogs.Where(item => item.LogName.ToLower().Contains(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.EndsWith:
                            currentLogs = currentLogs.Where(item => item.LogName.ToLower().EndsWith(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.Is:
                            currentLogs = currentLogs.Where(item => string.Equals(item.LogName, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
                            break;
                        case SelectorOperator.StartsWith:
                            currentLogs = currentLogs.Where(item => item.LogName.ToLower().StartsWith(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.Message:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Contains:
                            currentLogs = currentLogs.Where(item => item.Message.ToLower().Contains(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.EndsWith:
                            currentLogs = currentLogs.Where(item => item.Message.ToLower().EndsWith(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.Is:
                            currentLogs = currentLogs.Where(item => string.Equals(item.Message, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
                            break;
                        case SelectorOperator.StartsWith:
                            currentLogs = currentLogs.Where(item => item.Message.ToLower().StartsWith(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.Number:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Equals:
                            currentLogs = currentLogs.Where(item => item.Number == int.Parse(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.IsGreaterThan:
                            currentLogs = currentLogs.Where(item => item.Number > int.Parse(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.IsLessThan:
                            currentLogs = currentLogs.Where(item => item.Number < int.Parse(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.Source:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Contains:
                            currentLogs = currentLogs.Where(item => item.Source.ToLower().Contains(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.EndsWith:
                            currentLogs = currentLogs.Where(item => item.Source.ToLower().EndsWith(condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.Is:
                            currentLogs = currentLogs.Where(item => string.Equals(item.Source, condition.FieldValue as string, StringComparison.OrdinalIgnoreCase)).ToList();
                            break;
                        case SelectorOperator.StartsWith:
                            currentLogs = currentLogs.Where(item => item.Source.ToLower().StartsWith(condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
                case FieldItem.Type:
                    switch (condition.SelectorOperator)
                    {
                        case SelectorOperator.Is:
                            currentLogs = currentLogs.Where(item => item.Type == (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), condition.FieldValue as string)).ToList();
                            break;
                        case SelectorOperator.IsNot:
                            currentLogs = currentLogs.Where(item => item.Type != (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), condition.FieldValue as string)).ToList();
                            break;
                    }
                    break;
            }
        }

        return currentLogs;
    }
Run Code Online (Sandbox Code Playgroud)

示例查询可能如下所示:

条件选择器:

All of the conditions are met
Run Code Online (Sandbox Code Playgroud)

条件:

LogName Is "Application"
Message Contains "error"
Type IsNot "Information"
InstanceID IsLessThan 1934
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,SearchAll()由于嵌套switch语句,该方法很长且不易维护.但是,代码有效,我觉得这不是实现此设计的最优雅方式.有没有更好的方法来解决这个问题?也许通过找出降低switch层次结构复杂性的方法或使代码更通用?任何帮助/建议表示赞赏.

dem*_*key 1

我认为你确实需要两个 switch 语句,但它们不需要嵌套。您可以分离出对任何类型的对象通用的操作,然后传入您在运行时搜索的对象。

public static class SearchProvider
{
    static Func<object, bool> GetSearchMethod(SelectorOperator selectorOperator, string conditionFieldValue)
    {
        switch (selectorOperator)
        {
            //strings
            case SelectorOperator.Contains:
                return new Func<object, bool>(s => s.ToString().ToLower().Contains(conditionFieldValue));
            case SelectorOperator.StartsWith:
                return new Func<object, bool>(s => s.ToString().ToLower().StartsWith(conditionFieldValue));
            case SelectorOperator.EndsWith:
                return new Func<object, bool>(s => s.ToString().ToLower().EndsWith(conditionFieldValue));
            case SelectorOperator.Is:
                return new Func<object, bool>(s => string.Equals(s.ToString(), conditionFieldValue, StringComparison.OrdinalIgnoreCase));

            //numbers
            case SelectorOperator.Equals:
                return new Func<object, bool>(n => (long)n == long.Parse(conditionFieldValue));
            case SelectorOperator.IsGreaterThan:
                return new Func<object, bool>(n => (long)n > long.Parse(conditionFieldValue));
            case SelectorOperator.IsLessThan:
                return new Func<object, bool>(n => (long)n < long.Parse(conditionFieldValue));

            //type
            case SelectorOperator.TypeIs:
                return new Func<object, bool>(t => (EventLogEntryType)t == (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), conditionFieldValue));
            case SelectorOperator.TypeIsNot:
                return new Func<object, bool>(t => (EventLogEntryType)t != (EventLogEntryType)Enum.Parse(typeof(EventLogEntryType), conditionFieldValue));

            default:
                throw new Exception("Unknown selector operator");
        }
    }

    private static List<EventLogItem> SearchAll(List<EventLogItem> currentLogs, SearchQuery query)
    {
        foreach (SearchCondition condition in query.Conditions)
        {
            var search = GetSearchMethod(condition.SelectorOperator, condition.FieldValue as string);
            switch (condition.FieldName)
            {
                case FieldItem.Category:
                    currentLogs = currentLogs.Where(item => search(item.Category)).ToList();
                    break;
                case FieldItem.InstanceID:
                    currentLogs = currentLogs.Where(item => search(item.InstanceID)).ToList();
                    break;
                case FieldItem.LogName:
                    currentLogs = currentLogs.Where(item => search(item.LogName)).ToList();
                    break;
                case FieldItem.Message:
                    currentLogs = currentLogs.Where(item => search(item.Message)).ToList();
                    break;
                case FieldItem.Number:
                    currentLogs = currentLogs.Where(item => search(item.Number)).ToList();
                    break;
                case FieldItem.Source:
                    currentLogs = currentLogs.Where(item => search(item.Source)).ToList();
                    break;
                case FieldItem.Type:
                    currentLogs = currentLogs.Where(item => search(item.Type)).ToList();
                    break;
            }
        }
        return currentLogs;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我这么晚发布是因为 SO 服务器崩溃了,然后我就去睡觉了:(
因此它类似于 @dasblinkenlight 的答案。