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

时间:2013-01-18 08:41:30

标签: c# .net enums mapping

我正在网上预订网站(航空公司)工作,我想根据某些设置验证用户/客户选择的路线是否有效。现有的代码使用了大量的枚举,我发现自己做了很多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 =路线必须仅来自指定的出发站和到达站,反之亦然(ex NY-JP或JP-NY)
From =从特定车站到任何路线(前AU- 全部
To =到特定电台的任何路线(前全部 -AU)

如果您注意到,上述所有条件中的.Any都是相同的,只是略有变化。如果可能,我想减少代码冗余。我使用了KeyValuePair所以我有一个单一数据类型的出发站和到站。

关于如何让这段代码变得更乱/更漂亮的任何想法?我知道我也硬编码IsRouteValid(),但我100%确定Directionality只能有3种可能的组合。另一方面,RegionType可能有几个组合,如站 - 站,站 - 国家,国家 - 站,国家 - 国家等。

预期输出:

第一条路线的有效/真(NY-JP)
第二条路线无效/错误(NY-AU)
第三条路线的有效/真(JP-NY)[由DirectionalityBetween]

感谢您阅读此长篇查询,并提前感谢您的反馈和建议。

类似帖子:

Enum and Dictionary

1 个答案:

答案 0 :(得分: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 enum,并且有一段时间出发站和到达站将具有相同的RegionType enum。 (即站台,国家和全部)。

// 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我需要一种方法来计算重复的排列,所以我用Google搜索而不是写一个。 (呵呵)我使用排列的原因是为了避免对RegionType可能具有的所有可能组合进行硬编码。我将Alex Regueiro的方法修改为生成器,以便我可以使用Linq。有关置换和组合的更新,请参阅this very excellent math stackexchange post

我修改了IsRouteValid()来处理值RegionType.All的{​​{1}}。

以下是修改后的版本:

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");
    }

结果:

expected results screenshot

注意:

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())); } } Linqpad扩展名。