2013-01-18 6 views
5

Sto lavorando su un sito di prenotazione online (compagnia aerea) e voglio verificare se il percorso scelto dall'utente/cliente è valido in base ad alcune impostazioni. I codici esistenti usano un sacco di enumerazioni e mi sono trovato a fare un sacco di if/if else/else per mappare un enum specifico ad una particolare azione che volevo accadesse. 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?Mappare enum a una funzione/azione utilizzando il metodo enum specifico

Ecco una versione semplificata del codice applicazione utilizzando gli stessi nomi di classe valori/enum, ecc dal reale applicazione:

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

Ecco qualche esempio dell'uso:

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

Alcuni codice di validazione I ha scritto per diminuire l'accoppiamento annidato 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; 
} 

Ed ecco il codice disordinato voglio cambiare:

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 = stazione di partenza/origine
stazione TravelRegionCode = arrivo/destinazione
Between = percorsi devono essere solo dal data stazione di partenza e stazione di arrivo e viceversa (ex NY-JP o JP-NY)
From = da stazione particolare a qualsiasi rotta (ex AU- Tutti)
To = eventuali rotte per una particolare stazione (ex Tutti -AU)

Se si potesse notare, la .Any di tutte le condizioni di cui sopra sono gli stessi con lievi modifiche. Voglio ridurre la ridondanza del codice, se possibile. Ho usato KeyValuePair quindi ho sia la stazione di partenza che la stazione di arrivo su un singolo tipo di dati.

Qualche idea su come posso rendere questo codice meno disordinato/bello? So che ho anche programmato il codice IsRouteValid() ma sono sicuro al 100% che Directionality potrebbe avere solo le 3 combinazioni possibili. RegionType d'altra parte potrebbe avere diverse diverse combinazioni come Stazione-Stazione, Stazione-Country, Country Stazione, Paese-Paese ecc

Output previsto:

Valido/True per primo itinerario (NY- JP)
Invalid/falso per il secondo percorso (NY-AU)
valido/vero per il terzo percorso (JP-NY) [dal Directionality è Between]

grazie per aver letto questa lunghissima query e grazie in anticipo per il tuo feedba ck e suggerimenti.

post simile:

Enum and Dictionary

risposta

17

Un modo per gestire tali dizionari enum-azione-Mapping sta usando.Ecco un esempio:

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

Naturalmente è anche possibile utilizzare Func invece di Action per gestire i parametri.

Edit:

Come non si sta utilizzando solo un'enumerazione, ma coppie di enumerazioni che avrebbe dovuto modificare l'esempio precedente a forse gestire una Tuple o di un altro modo per aggregare i valori enum.

4

seguito @MatthiasG suggerimento, ecco il codice che avevo finito per scrivere:

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

ho usato List<KeyValuePair<RegionType, string>> perché Dictionary non consente duplicati delle chiavi. Le mie chiavi sono RegionType enum e ci sarà un orario in cui entrambe le stazioni di partenza e di arrivo avranno lo stesso RegionType enum. (cioè Stazione-Stazione, Paese-Paese e Tutto-Tutto).

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

     } 
    } 
} 

Ok, ho imbrogliato. : P Avevo bisogno di un metodo che calcolasse le permutazioni con ripetizioni, quindi, invece di scriverne uno, ho cercato su Google. (hehe) Il motivo per cui ho usato le permutazioni è di evitare di codificare con difficoltà tutte le combinazioni possibili che potrebbero avere RegionType. Ho modificato il metodo di Alex Regueiro per essere un generatore in modo da poter usare Linq per questo. Per un aggiornamento su permutazione e combinazione, vedere this very excellent math stackexchange post.

Ho modificato IsRouteValid() per gestire RegionType.All il cui valore è null.

Ecco la versione modificata:

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

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

Ho creato questo codice per dimostrare come ho ottenuto il risultato che è lo stesso che i miei risultati attesi di cui sopra.

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

Risultati:

expected results screenshot

Nota:

.Dump() è un'estensione Linqpad.