2015-01-27 15 views
6

Istnieje kilka wątków na zakresach wartości w wyliczeniach (nie jest to możliwe). Ale mam następujący problem i szukam najlepszego rozwiązania, w którym żaden z podanych mi produktów naprawdę mnie nie zadowalał.C# alternatywa do wyliczenia dla relacji n: m

Specyfikacja protokółu mówi, że bajt [x] z komunikatu, typu komunikatu, posiada następujące możliwe wartości (wartości Fantasy):

0x00 = get 
0x01 = set 
0x02 to 0xFF = identify 

więc istnieje tylko 3 Warianty logicznych, co najlepiej radzić sobie w wyliczaniu. Ale jedna z n opcji logicznych ma m różne odpowiedniki numeryczne, których nie można rozpatrywać w wyliczeniu.

Jakie jest najlepsze (najczystsze) rozwiązanie tego problemu? mógłbym zbudować klasę

class MessageType { 
    public enum MessageTypeEnum { 
     get = 0x00, 
     set = 0x01, 
     identify = 0x02 
    } 

    public static MessageTypeEnum getLogicalValue (byte numericalValue) 
    { 
     if (numericalValue < 0x02) 
      return (MessageTypeEnum(numericalValue)); 
     else 
      return MessageTypeEnum.identify; 
    } 
} 

mogę również utworzyć klasę bez wyliczenia, ale z członków statycznych.

czy inaczej jest jeden problem: Jeśli ktoś próbuje wysyła pakiet, może on wykorzystać

if (messageBytes[x] == (byte)MessageTypeEnum.identify) { 
    // do stuff 
} 

Ale messageByte [x] może być coś między 0x02 a 0xFF, więc „trafienia” wartość podaną w enum byłoby czystym szczęściem. Z drugiej strony chcę, aby enum (lub statyczny członek) był jawny dla łatwego budowania wiadomości.

Czy mogę w jakiś sposób wymusić użycie mojej funkcji getLogicalValue()? Czy istnieje bardziej eleganckie rozwiązanie?

Wszystko, czego chcę, to łatwy i dobrze zorganizowany sposób łączenia wartości logicznych z wartościami liczbowymi w relacji n: m. Szczególnie, że dany protokół ma wiele takich przypadków i chciałabym zachować porządek w swoim kodzie.

Dzięki za pomoc i czasu :)

Janis

+0

Przypuszczam, że masz na myśli 'messageBytes [x] == (byte) ...' zamiast '=', co byłoby zadaniem. Nigdy nie można w pełni uniemożliwić innym osobom rzucania jednym typem do drugiego (chyba że przesłonimy operatora obsady i wyrzucimy wyjątek). Jeśli ktoś chce nadużywać twojego kodu, zrobi to. – Krumelur

+0

Tak, naprawdę miałem na myśli "==". Ten błąd przydarzy mi się, gdy pochodzę z innych języków i bezpośrednio wpisałem kod w oknie komentarza. Co do nadużywania kodu: Chodzi o to, że sam mógłbym popełnić ten błąd podczas korzystania z pliku .dll, który chcę zbudować, ponieważ Enums (zwykle) są funkcją dwukierunkową (wartość logiczna <=> wartość liczbowa) – Janis

Odpowiedz

4

Polecam, aby zrezygnować z używania wyliczeń i utworzyć niestandardowy typ dla tego.

Może to być zarówno struct, jak i class; to nie ma znaczenia. Można rozwiązać problem równości przez przeciążenie operatora == i zapewnienie niestandardowej implementacji.

coś takiego:

public class MessageType 
{ 
    private readonly byte value; 
    private MessageType(byte value) 
    { 
     this.value = value; 
    } 

    public static readonly MessageType Get = new MessageType(0); 
    public static readonly MessageType Set = new MessageType(1); 
    public static readonly MessageType Identify = new MessageType(2); 


    public static bool operator ==(MessageType m, byte b) 
    { 
     if (object.ReferenceEquals(m, null)) 
      return false; 

     if (m.value == 2 && b >= 2 && b <= 0xff)//I think <= check is redundant 
      return true; 
     return m.value == b; 
    } 

    public static bool operator !=(MessageType m, byte b) 
    { 
     return !(m == b); 
    } 
    //Need to implement Equals, GetHashCode etc 
} 

Nie zapomnij wdrożyć Equals i GetHashCode dla spójności w realizację równa.

+0

To jest najlepsza jak dotąd odpowiedź, a także bliska temu, co sam wymyśliłem. Ale ja szukałem bardziej eleganckiego rozwiązania. Chodzi o to, że mam około 20 takich wyliczeń w protokole, które byłyby CAŁKOWITĄ wersją kodu w porównaniu z prostymi wyliczeniami. Nie uznałem tej odpowiedzi za rozwiązanie, ponieważ wciąż mam nadzieję na coś lepszego (choć wątpię). Jeszcze raz dziękuję :) – Janis

+0

@Janis Jestem również zainteresowany poznaniem lepszego sposobu, jeśli ktoś może przyjść. Nie będę się tym przejmował, ponieważ 20 nie brzmi dla mnie jak ogromny numer. Jest to jednorazowa praca - w porządku. Jeśli masz takie same zasady dla różnych wyliczeń, możesz je ponownie użyć (jeśli to możliwe). –

+0

Podejście, które teraz obserwuję, pochodzi od kolegi: użyję Enums i przypisze 0x02 wartość logiczną "identificationRangeStart" i 0xFF wartość "identificationRangeEnd" (lub podobne). Mam nadzieję, że każdy, kto używa Enum zobaczy, że są to nietypowe wartości i sprawdzi komentarz, który wyjaśnia. Dzięki za pomoc, twoje rozwiązanie jest wciąż najczystsze :) – Janis

3

Właściwie chciałbym użyć wartości enum jako identyfikatory, a nie tylko jako rzeczywiste wartości. Coś takiego:

class MessageType { 
    public enum MessageTypeEnum { 
     get, 
     set, 
     identify 
    } 

    public static MessageTypeEnum getLogicalValue (byte numericalValue) 
    { 
     if (numericalValue == 0x00) 
      return MessageTypeEnum.get; 
     else if (numericalValue == 0x01) 
      return MessageTypeEnum.set; 
     else 
      return MessageTypeEnum.identify; 
    } 
} 

i używać go w ten sposób:

if (MessageType.getLogicalValue(messageBytes[x]) == MessageTypeEnum.identify) { 
    // do stuff 
} 

Można oczywiście użyć switch zamiast if... else if..., to zależy od osobistych preferencji i potrzeb.

Jak stwierdzono w innej odpowiedzi, jeśli chcesz użyć bezpośredniego porównania, musisz utworzyć niestandardową klasę i zaimplementować porównanie równości z potrzebnymi.

+0

W rzeczywistości instrukcje "else if" nie są wymagane. jeśli (wartość liczbowa == 0x00) zwróci MessageTypeEnum.get właśnie tam, zwróci, jeśli ocena jest prawdziwa. – hmartinezd

+2

@hmartinezd tak, wiem, ale IMO jest bardziej czytelny w ten sposób. Ponownie, myślę, że to tylko kwestia osobistych preferencji. –

+0

Zgadzam się z tobą, chciałem tylko wskazać alternatywę. To zawsze kwestia preferencji. – hmartinezd

0

można byłaby podpis metody i wykonaj następujące czynności i powie to, które enum wg nazwy to faktycznie przekazując w wyglądzie wartości w sposób po prostu refactored dla ciebie i jak nazywają go poniżej

public static string getLogicalValue(byte numericalValue) 
{ 
    Type type = numericalValue.GetType(); 
    var name = Enum.GetName(typeof(MessageTypeEnum), numericalValue); 
    if (numericalValue < 0x02) 
     return name; 
    else 
     return MessageTypeEnum.identify.ToString(); 
} 

Wywołanie metody statycznej jak ten

var enumValue = getLogicalValue(0x02); 

ciąg powrót będzie identify lub jeśli chcesz zrobić oświadczenie switch/case można wykonać następujące czynności oraz

public static string getLogicalValue(byte numericalValue) 
    { 
     Type type = numericalValue.GetType(); 
     var name = Enum.GetName(typeof(MessageTypeEnum), numericalValue); 
     switch(numericalValue) 
     { 
      case 0x00: 
       { 
        name= (MessageTypeEnum.get.ToString()); 
        break; 
       } 
      case 0x01: 
       { 
        name= (MessageTypeEnum.set.ToString()); 
        break; 
       } 

      default: 
       { 
        name= (MessageTypeEnum.identify.ToString()); 
        break; 
       } 
     } 
     return name; 
    } 
+0

Wszystko, co musisz zrobić, to rzucić możliwie złe wyliczenie na ciąg. Twój kod nadal ma oryginalny problem: wartości liczbowe mogą prowadzić do nieprawidłowej wartości logicznej (w twoim przypadku ciąg pusty). Dlatego uważam to za gorsze rozwiązanie niż moje własne. Dzięki za podejście. I tak: twoje "Type type = ..." nigdy nie jest używane i można je usunąć. – Janis

+0

@chuex to prawda, nie napisałem wszystkich scenariuszy, jednak zmienię odpowiedź i użyję domyślnego. dzięki za wskazanie tego .. – MethodMan