如何使我的开关语句性能更高



我有一个针对多种枚举类型的 switch 语句,我根据类型检查与该类型对应的时间戳是否在提供的日期范围内。我使用了 10 个开关,想知道是否有性能更高的查询函数。

是否可以使用 selectmany 或 linq 中的 where 来使其性能更高?

private Event[] FilterNotifications(Event[] eventResponseItems, DateTime beginDate, DateTime endDate)
{
    var eventList = eventResponseItems.ToList();
    foreach (var eEvent in eventList)
    {
        switch (eEvent.EventType)
        {
            case "RouteStarted":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.RouteInfo.RouteTimestamps.Started))
                    eventList.Remove(eEvent);
                break;
            case "RouteDeparted":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.RouteInfo.RouteTimestamps.Arrived))
                    eventList.Remove(eEvent);
                break;
            case "RouteArrived":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.RouteInfo.RouteTimestamps.Completed))
                    eventList.Remove(eEvent);
                break;
            case "RouteCompleted":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.RouteInfo.RouteTimestamps.Departed))
                    eventList.Remove(eEvent);
                break;
            case "StopArrived":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.StopInfo.ArrivalTimeStamp))
                    eventList.Remove(eEvent);
                break;
            case "StopDeparted":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.StopInfo.DepartureTimeStamp))
                    eventList.Remove(eEvent);
                break;
            case "StopServicing":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.StopInfo.ArrivalTimeStamp))
                    eventList.Remove(eEvent);
                break;
            case "StopCancelled":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.StopInfo.DepartureTimeStamp))
                    eventList.Remove(eEvent);
                break;
            case "RouteStatusChanged":
                if (!InRange(beginDate, endDate, eEvent.EventInfo.StopInfo.ArrivalTimeStamp))
                    eventList.Remove(eEvent);
                break;
        }
    }
    return eventList.ToArray();
}
private bool InRange(DateTime beginTime, DateTime endTime, string timeStamp)
{
    DateTime timeStmp = DateConverter.ToInternal(timeStamp).Value;
    if ( timeStmp >= beginTime)
    {
        if (endTime != new DateTime() && timeStmp <= endTime)
        {
            return true;
        }
        return true;
    }
    return false;
}

在您的方法中,实际上没有太多可以优化性能的,因为它不使用任何复杂的迭代。

从可读性的角度来看,您可以使用字典。这也将允许您通过使用 .Where() 来避免完全具体化返回的数组。

您将需要一个稍微复杂的Func<>如下所示,以便将每个事件的事件信息枚举转换为时间戳字符串。

var RouteControl = new Dictionary<string,Func<Event,string>>()
{
    { "RouteStarted"       , eEvent => eEvent.EventInfo.RouteInfo.RouteTimestamps.Started },
    { "RouteDeparted"      , eEvent => eEvent.EventInfo.RouteInfo.RouteTimestamps.Arrived },
    { "RouteArrived"       , eEvent => eEvent.EventInfo.RouteInfo.RouteTimestamps.Completed },
    { "RouteCompleted"     , eEvent => eEvent.EventInfo.RouteInfo.RouteTimestamps.Departed },
    { "StopArrived"        , eEvent => eEvent.EventInfo.StopInfo.ArrivalTimeStamp },
    { "StopDeparted"       , eEvent => eEvent.EventInfo.StopInfo.DepartureTimeStamp },
    { "StopServicing"      , eEvent => eEvent.EventInfo.StopInfo.ArrivalTimeStamp },
    { "StopCancelled"      , eEvent => eEvent.EventInfo.StopInfo.DepartureTimeStamp },
    { "RouteStatusChanged" , eEvent => eEvent.EventInfo.StopInfo.ArrivalTimeStamp }       
};
return eventResponseItems.Where(eEvent => 
    !RouteControl.ContainsKey(eEvent.EventType) ||
    InRange(beginDate, endDate, RouteControl[eEvent.EventType](eEvent))
).ToArray();

我更关心代码的可维护性而不是性能。如果我们一开始只是尝试删除重复的代码,并使剩余的代码易于理解,我最终会得到这样的结果:

private Event[] FilterNotifications(Event[] eventResponseItems, DateTime beginDate, DateTime endDate)
{
    return eventResponseItems
        .Where(e => InRange(beginDate, endDate, GetEventRouteTimeStampFromEventType(e.EventInfo.RouteInfo.RouteTimestamps, e.EventType)))
        .ToArray();
}
private string GetEventRouteTimeStampFromEventType(RouteTimeStamps routeTimeStamps, string eventType)
{
    switch (eventType)
    {
        case "RouteStarted":
        case "StopCancelled":
            return routeTimestamps.Started;
        case "RouteDeparted":
            return routeTimestamps.Arrived;
        case "RouteArrived":
            return routeTimestamps.Completed;
        case "RouteCompleted":
            return routeTimestamps.Departed;

        case "StopDeparted":
            return routeTimestamps.DepartureTimeStamp;
        case "RouteStatusChanged":
        case "StopServicing":
        case "StopArrived":
            return routeTimestamps.ArrivalTimeStamp;
        default: throw new ArgumentOutOfRangeException();
        }
    }
}

而且,事实上,这也恰好获得了更好的性能(因为您正在过滤,而不是创建列表并从中删除项目(。所以这是一场不错的胜利。

请注意,我假设您有一个有限的事件类型列表,并且您希望在看到其他任何内容时抛出异常(当您选择 switch 语句时通常是这种情况(。如果不是这种情况,您可以执行诸如返回 null 之类的操作,并相应地调整剩余的代码。

List.Remove 在 O(n( 中,所以整个循环都在 O(n^2( 中。如果在循环访问仅包含所需元素的第一个列表时填充第二个列表,则可以提高性能。

最新更新