【问题标题】:Map enum to a function/action using enum-specific method使用特定于枚举的方法将枚举映射到函数/动作
【发布时间】:2013-01-01 22:34:18
【问题描述】:

我正在开发一个在线预订网站(航空公司),我想根据某些设置验证用户/客户选择的路线是否有效。现有代码使用了很多枚举,我发现自己做了很多 if/if else/else 来将特定枚举映射到我想要发生的特定操作。 What I want to do is to write a enum-specific method that would do the mapping for me. Is there any standard way to do this?

这是一个简化版本的应用代码,使用与真实应用相同的类名/枚举值等:

// 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)
第二条路线无效/错误 (NY-AU)
第三条路线(JP-NY)的有效/真[因为DirectionalityBetween]

感谢您阅读这个很长的查询,并提前感谢您的反馈和建议。

类似帖子:

Enum and Dictionary

【问题讨论】:

    标签: c# .net enums mapping


    【解决方案1】:

    根据@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&lt;KeyValuePair&lt;RegionType, string&gt;&gt;,因为Dictionary 不允许密钥重复。我的钥匙是RegionType enum 的,有时出发站和到达站都会有相同的RegionType enum。 (即 Station-Station、Country-Country 和 All-All)。

    // 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 了。有关排列和组合的复习,请参阅this very excellent math stackexchange post

    我修改IsRouteValid()来处理RegionType.All,其值为null

    这是修改后的版本:

    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 扩展。

    【讨论】:

      【解决方案2】:

      处理这种枚举-动作-映射的一种方法是使用字典。这是一个例子:

      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 或其他聚合枚举值的方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-11
        • 1970-01-01
        相关资源
        最近更新 更多