使用特定于枚举的方法将枚举映射到函数/操作

7
我正在开发一个在线预订网站(航空公司),我希望验证用户/客户选择的路线是否符合一些设置。现有代码大量使用枚举类型,我发现自己不断地使用if/if else/else语句将特定的枚举映射到我想要执行的特定操作。
我的想法是编写一个特定于枚举类型的方法来为我完成该映射。是否有任何标准方法可以实现这个想法?
以下是使用真实应用程序中相同的类名/枚举值等的应用程序代码的简化版本:
// real app has 9 members, shortened for simplicity's sake
public enum RegionType
{
    Station,
    Country,
    All
}

public enum Directionality
{
    Between,
    From,
    To
}

// simplified version
public class Flight
{
     public RegionType RegionType { get; set; }
     public RegionType TravelRegionType { get; set; }
     public string RegionCode { get; set; }
     public string TravelRegionCode { get; set; }
     public string RegionCountryCode { get; set; }
     public string TravelRegionCountryCode { get; set; }
     public Directionality Directionality { get; set; }
}

以下是一些使用示例:
    // valid flight
    Flight flight = new Flight()
    {
        RegionCode = "NY",
        CountryCode = "JP",
        RegionType = RegionType.Station,
        TravelRegionType = RegionType.Country,
        Directionality = Directionality.Between
    };

    // these are the station code/country code that user selected
    // needs to be validated against the Flight object above
    var userSelectedRoutes = new List<KeyValuePair<string, string>>() 
    { 
        new KeyValuePair<string, string>("NY", "JP"),
        new KeyValuePair<string, string>("NY", "AU"),
        new KeyValuePair<string, string>("JP", "NY")
    };

我写了一些代码验证以减少嵌套的if/else if/else枚举匹配:

private bool IsRouteValid(Directionality direction, string origin, 
                          string destination, string departure, string arrival)
{
    // both departure station and arrival station
    if (direction == Directionality.Between)
    {
        return (origin.Equals(departure, StringComparison.OrdinalIgnoreCase) 
          && destination.Equals(arrival, StringComparison.OrdinalIgnoreCase)
               || origin.Equals(arrival, StringComparison.OrdinalIgnoreCase) 
          && destination.Equals(departure, StringComparison.OrdinalIgnoreCase));
    }
    else if (direction == Directionality.From)
    {
            return (origin.Equals(departure, 
                    StringComparison.OrdinalIgnoreCase));   
    }
    else if (direction == Directionality.To)
    {
            return (destination.Equals(arrival, 
                    StringComparison.OrdinalIgnoreCase));
    }

    return false;
}

这是我想要修改的混乱代码:

if (flight.RegionType == RegionType.Station 
    && flight.TravelRegionType == RegionType.Country)
{
     return userSelectedRoutes.Any(route => 
            IsRouteValid(flight.Directionality, route.Key, route.Value,
            flight.RegionCode, flight.TravelRegionCode));
}
else if (flight.RegionType == RegionType.Country 
&& flight.TravelRegionType == RegionType.Station)
{
    return userSelectedRoutes.Any(route => 
           IsRouteValid(flight.Directionality, route.Key, route.Value,
           flight.CountryCode, flight.RegionCode));
}
else if (flight.RegionType == RegionType.Station 
&& flight.TravelRegionType == RegionType.Station)
{
    return userSelectedRoutes.Any(route => 
           IsRouteValid(flight.Directionality, route.Key, route.Value,
                       flight.RegionCode, flight.TravelRegionCode));
}
else if (flight.RegionType == RegionType.Station 
&& flight.TravelRegionType == RegionType.All)
{
    return userSelectedRoutes.Any(route => 
           IsRouteValid(flight.Directionality, route.Key, route.Value,
                       flight.RegionCode, route.Value));
}
else if (flight.RegionType == RegionType.All 
&& flight.TravelRegionType == RegionType.Station)
{
    return userSelectedRoutes.Any(route =>
           IsRouteValid(flight.Directionality, route.Key, route.Value,
           route.Key, flight.TravelRegionCode));
}
else if (flight.RegionType == RegionType.All 
        && flight.TravelRegionType == RegionType.All)
{
    return true;
}
else
{
    return false;
} 

说明:

RegionCode = 出发站点/出发地
TravelRegionCode = 到达站点/目的地
Between = 路线必须只从给定的出发站点和到达站点以及相反方向进行(例如 NY-JP 或 JP-NY)
From = 从特定车站到任何路线(例如 AU-All
To = 任何路线到某个特定车站(例如 All-AU)

如果您能注意到,上述所有条件中的 .Any 均相同,只有轻微的区别。 如果可能,我希望减少代码冗余。 我使用了 KeyValuePair,因此单个数据类型上具有出发站点和到达站点。

您对如何使这段代码更简洁/优美有什么想法吗?我知道我也硬编码了 IsRouteValid(),但我100%确定 Directionality 只能有3种可能的组合。RegionType 另一方面可以有多种组合,例如 Station-Station、Station-Country、Country-Station、Country-Country 等。

期望输出:

第一条路径(NY-JP)有效/True
第二条路径(NY-AU)无效/False
第三条路径(JP-NY)有效/True [因为 DirectionalityBetween]

感谢您阅读这个非常长的查询,感谢您提前给出反馈和建议。

类似的帖子:

枚举和字典

2个回答

23

处理此类枚举动作映射的一种方法是使用字典。以下是一个示例:

public enum MyEnum
{
    EnumValue1,
    EnumValue2,
    EnumValue3
}

private IDictionary<MyEnum, Action> Mapping = new Dictionary<MyEnum, Action>
    {
        { MyEnum.EnumValue1, () => { /* Action 1 */ } },
        { MyEnum.EnumValue2, () => { /* Action 2 */ } },
        { MyEnum.EnumValue3, () => { /* Action 3 */ } }
    };

public void HandleEnumValue(MyEnum enumValue)
{
    if (Mapping.ContainsKey(enumValue))
    {
        Mapping[enumValue]();
    }
}
当然,您也可以使用Func而不是Action来处理参数。 编辑: 由于您不仅在使用一个枚举,而是一对枚举,因此您需要调整上面的示例,可能要处理Tuple或另一种聚合枚举值的方式。

4

根据@MatthiasG的建议,这是我最终写的代码:

private List<KeyValuePair<RegionType, string>> 
                GetRegionTypeAndValueMapping(Flight flight, 
                                             RegionType regionType,
                                             RegionType travelRegionType)
{
    var mapping = new List<KeyValuePair<RegionType, string>>();
    if(regionType == RegionType.Station)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.Station, flight.RegionCode));
    }
    else if(regionType == RegionType.Country)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.Country, flight.RegionCountryCode));
    }
    else if(regionType == RegionType.All)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.All, null));
    }

    if(travelRegionType == RegionType.Station)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.Station, flight.TravelRegionCode));
    }
    else if(travelRegionType == RegionType.Country)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.Country, 
                           flight.TravelRegionCountryCode));
    }
    else if(travelRegionType == RegionType.All)
    {
        mapping.Add(new KeyValuePair<RegionType, string>
                          (RegionType.All, null));
    }

    return mapping;
}

我使用了List<KeyValuePair<RegionType, string>>,因为Dictionary不允许键重复。我的键是RegionType枚举的,到达站和出发站有时会具有相同的RegionType枚举(即站-站,国家-国家和全部-全部)。

// Copyright (c) 2010 Alex Regueiro
// Licensed under MIT license, available at 
// <http://www.opensource.org/licenses/mit-license.php>.
// Published originally at 
// <http://blog.noldorin.com/2010/05/combinatorics-in-csharp/>.
// Version 1.0, released 22nd May 2010.

// modified by moi to be a generator 
public static IEnumerable<T[]> GetPermutations<T>(IList<T> list, 
                                                  int? resultSize, 
                                                  bool withRepetition)
{
    if (list == null)
    {
        throw new ArgumentNullException("Source list is null.");
    }

    if (resultSize.HasValue && resultSize.Value <= 0)
    {
        throw new ArgumentException("Result size must be any 
                                     number greater than zero.");
    }

    var result = new T[resultSize.HasValue ? resultSize.Value : list.Count];
    var indices = new int[result.Length];
    for (int i = 0; i < indices.Length; i++)
    {
        indices[i] = withRepetition ? -1 : i - 1;
    }

    int curIndex = 0;
    while (curIndex != -1)
    {
        indices[curIndex]++;
        if (indices[curIndex] == list.Count)
        {
            indices[curIndex] = withRepetition ? -1 : curIndex - 1;
            curIndex--;
        }
        else
        {
            result[curIndex] = list[indices[curIndex]];
            if (curIndex < indices.Length - 1)
            {
                curIndex++;
            }
            else
            {
                yield return result;
            }

        }
    }
}

好的,我作弊了:P我需要一种计算带有重复的排列的方法,所以我没有编写一个,而是在谷歌上搜索。我使用排列的原因是避免硬编码所有可能的RegionType组合。我修改了Alex Regueiro的方法,使其成为生成器,这样我就可以为它使用Linq。如果想恢复对排列和组合的了解,请参见这个非常好的数学stackexchange post
我修改了IsRouteValid()来处理值为nullRegionType.All
以下是修改后的版本:
private bool IsRouteValid(Directionality direction, string origin, 
                          string destination, string departure, 
                          string arrival)
{
     // ** == All stations/countries
     if ((origin == null && departure == "**") &&
         (destination == null && arrival == "**"))
     {
         return true;
     }
     else if (origin == null && departure == "**")
     {
        return destination.Equals(arrival, StringComparison.OrdinalIgnoreCase);
     }
     else if (destination == null && arrival == "**")
     { 
       return origin.Equals(departure, StringComparison.OrdinalIgnoreCase);
     }

    // both departure station and arrival station
    if (direction == Directionality.Between)
    {
            return (origin.Equals(departure,
                    StringComparison.OrdinalIgnoreCase) && 
                    destination.Equals(arrival, 
                    StringComparison.OrdinalIgnoreCase) || 
                    origin.Equals(arrival, 
                    StringComparison.OrdinalIgnoreCase) &&         
                    destination.Equals(departure,
                    StringComparison.OrdinalIgnoreCase));
    }
    else if (direction == Directionality.From)
    {
        return (origin.Equals(arrival, StringComparison.OrdinalIgnoreCase));
    }
    else if (direction == Directionality.To)
    {
        return (destination.Equals(departure, 
               StringComparison.OrdinalIgnoreCase));
    }

    return false;
}

开始表演!

RegionType[] allRegionTypes = (RegionType[]) 
                              Enum.GetValues(typeof(RegionType));

var validFlights = 
      GetPermutations<RegionType>(allRegionTypes, 2, true)
         .Select(perm => new 
                         { 
                           RegionType = perm.First(),
                           TravelRegionType = perm.Last() 
                         })
         .Where(result => result.RegionType == flight.RegionType &&  
                          result.TravelRegionType ==                            
                          flight.TravelRegionType)
         .Select(map => 
                   GetRegionTypeAndValueMapping(flight,
                     map.RegionType, 
                     map.TravelRegionType));

    // same functionality as my previous messy code
    // validates all flights selected by user
    // it doesn't matter if not all flights are valid
    // as long as one of them is
    foreach(var validFlight in validFlights)
    {
        userSelectedRoutes.Any(kvp => IsRouteValid(flight.Directionality, 
                                                   kvp.Key, 
                                                   kvp.Value, 
                                                   validFlight.First().Value,
                                                   validFlight.Last().Value))
                                      .Dump("Any Flight");
    }

我创建了这段代码来演示如何得到与我预期结果相同的结果。
    foreach(var route in userSelectedRoutes)
    {
        foreach(var validFlight in validFlights)
        {
            bool condition = IsRouteValid(flight.Directionality, 
                                          route.Key, 
                                          route.Value, 
                                          validFlight.First().Value, 
                                          validFlight.Last().Value);

            Console.WriteLine(string.Format("{0}-{1} {2}", 
                                               route.Key,     
                                               route.Value, 
                                               condition.ToString()));
        }
    }

结果:

期望结果截图

注意:

.Dump()Linqpad 的扩展。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接