2013-01-18 17 views
5

Pracuję na stronie rezerwacji online (linie lotnicze) i chcę sprawdzić, czy trasa wybrana przez użytkownika/klienta jest ważna zgodnie z niektórymi ustawieniami. Istniejące kody używają wielu wyliczeń i odkryłem, że robię dużo, jeśli/jeśli jeszcze/else, aby zmapować konkretne wyliczenie na konkretne działanie, które chciałem zrealizować. 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?Mapowanie do funkcji/działania przy użyciu metody specyficznej dla enum

Oto uproszczona wersja kodu aplikacji przy użyciu tych samych nazw klas/wartości enum itp od rzeczywistych aplikacji:

// 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; } 
} 

Oto niektóre wykorzystanie próbki:

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

Niektóre kod walidacji I napisał, aby pomniejszyć zagnieżdżone, jeśli/else jeśli/else wylicza dopasowanie:

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

A oto niechlujny kod Chcę zmienić:

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

Legenda:

RegionCode = stacja wyjazdu/pochodzenia
stacja TravelRegionCode = przyjazd/przeznaczenia
Between = trasy musi być tylko z daną stację odlotów i przylotów i odwrotnie (np. NY-JP lub JP-NY)
From = z konkretnej stacji na dowolne trasy (np. AU-Wszystkie)
To = wszelkie drogi do konkretnej stacji (ex Wszystko -AU)

Jeśli można wypowiedzenia .Any wszystkich powyższych warunków są takie same z niewielkimi zmianami. Chcę, jeśli to możliwe, zmniejszyć nadmiarowość kodu. Użyłem KeyValuePair, więc mam zarówno stację odlotową, jak i przybycie na jednym typie danych.

Jakieś pomysły, w jaki sposób mogę uczynić ten kod mniej niechlujnym/pięknym? Wiem, że również mocno zakodowałem IsRouteValid(), ale jestem w 100% pewien, że Directionality może mieć tylko 3 możliwe kombinacje. RegionType z drugiej strony może mieć kilka kilka kombinacji jak Station-Station, Station-country, country-Station, Country-Country itp

oczekiwany wynik:

Ważny/true dla pierwszej trasy (nY- JP)
Nieprawidłowy/Fałsz do drugiej drogi (NY-AU)
Ważny/true dla trzeciej drogi (JP-NY) [od Directionality jest Between]

Dziękuję za przeczytanie tego bardzo dużo zapytań i dzięki z góry dla twojego feedba ck i sugestie.

podobnym stanowisku:

Enum and Dictionary

Odpowiedz

17

Droga do obsługi takich enum-action-Mappings korzysta ze słowników.Oto przykład:

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](); 
    } 
} 

Oczywiście można również użyć Func zamiast Action obsługiwać parametry.

Edit:

jak nie jesteś tylko przy użyciu jednego wyliczanie ale pary wyliczeń trzeba by dostosować się do przykładu powyżej może obsłużyć Tuple lub w inny sposób agregowanie wartości enum.

4

Po @MatthiasG sugestii, oto kod ja skończyło się na piśmie:

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

użyłem List<KeyValuePair<RegionType, string>> ponieważ Dictionary nie pozwala kluczowych duplikatów. Moje klucze mają numer RegionType enum i nadejdzie czas, w którym zarówno stacje odlotów, jak i przylotów będą miały ten sam numer RegionType enum. (to jest Station Station, Country Country i 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; 
      } 

     } 
    } 
} 

Okay, oszukałem. : P Potrzebowałem metody, która będzie obliczać permutacje z powtórzeniami, więc zamiast pisać jedną, szukałem go. (hehe) Powodem, dla którego użyłem permutacji jest uniknięcie twardego kodowania wszystkich możliwych kombinacji, które mogą mieć. Zmodyfikowałem metodę Alex Regueiro na generator, dzięki czemu mogłem użyć do tego celu Linq. Aby dowiedzieć się więcej o Permutation and Combination, zobacz this very excellent math stackexchange post.

Zmodyfikowałem IsRouteValid() na obsługę RegionType.All, którego wartość to null.

Oto zmodyfikowana wersja:

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

To Show Time!

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

Ten kod został stworzony w celu zademonstrowania, w jaki sposób uzyskałem wynik, który jest taki sam jak moje oczekiwane wyniki powyżej.

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

Wyniki:

expected results screenshot

UWAGA:

.Dump() jest Linqpad przedłużenia.

Powiązane problemy