2015-11-19 10 views
5

To jest dziwaczne.Jeśli instrukcja nie działa zgodnie z oczekiwaniami dla łącznej wartości wyliczeniowej

Mam następujący kod ...

foreach (IScanTicket ticket in this) { 
    if (ticket.Status == TicketStatus.Created || ticket.Status == (TicketStatus.Transfered | TicketStatus.Created)) 
     return ticket; 
    } 
} 

Kiedy uruchamiam to, gdzie stan jest Created|Transferred, if wydaje się niepowodzeniem (nie to co to niby ma).

Ciekawostką jest to, że jeśli debuguję i przechodzę przez kod i obserwuję instrukcję, to zawsze w moim debugerze zwraca ona TRUE, ale nie przejdzie do bloku, kiedy przechodzę przez kod.

Dlaczego debugger wykaże, że instrukcja jest true, a mimo to nadal nie jest? To tak, jak debugger mówi mi bobry.

Czy ktoś kiedykolwiek tego doświadczył?

P.S. Używam studia Xamarin 5.9.7

+1

Jak to jest "TicketStatus"? Czy to jest wyliczenie z atrybutem "[Flags]"? – MarcinJuraszek

+0

Jaka jest wartość 'ticket.Status', gdy się nie powiedzie, ale spodziewasz się, że będzie" true "? –

+0

Interesujące ... jeśli zamienię enum join (np. '(TicketStatus.Created | TicketStatus.Transfered)') wydaje się działać. Bycie wylicznikiem to wyliczenie oparte na flagach "bajtowych", pomyślałbym, że nie miało to znaczenia? .. jakie pomysły? –

Odpowiedz

0

Podziękowania dla @MarcinJuraszek i @YeldarKurmangaliyev.

Wygląda na to, że atrybut nie był ustawiony na enum, jak początkowo sądziłem. Dodanie tego atrybutu powoduje teraz, że enum działa w obu kombinacjach.

Wygląda więc na to, że brak tego atrybutu wpływa na kolejność połączonych wartości wyliczeniowych.

+0

Wyliczenia bez zakodowanych wartości automatycznie numerują się same. W przypadku enum flagi lepiej jest wyraźnie zdefiniować wartości. Na przykład. 0, 0x1, 0x2, 0x4, 0x8, 0x16, 0x32, 0x64 .. itd. Jeśli twarde kodowanie wartości zadziała, czy ma na sobie [flagi], czy nie. Nie hardcoding ich może cię ugryźć później, ponieważ jeśli dodasz nową wartość w środku istniejących wartości, to zrównoważy stare wartości, które mogą zepsuć twoje dane (np. W bazie danych) i wytworzyć skośne wyniki w dół linii, która będzie nie są oczywiste, aby rozwiązać problem. –

+0

@Ryios Jeśli przechowujesz wartości wyliczeniowe w bazie danych, zawsze dobrze jest je skopiować. Jeśli nie, nie ma powodu. – Amasa

+0

Zawsze twardo koduję je na wypadek, gdyby kompilator tego nie zrobił. Technicznie mój kod był właściwy, ale realistycznie kompilator myślał o czymś innym. Po prostu sprowadzę to do dziwactwa w kompilatorze i pułapce, na którą trzeba uważać. Mogę wyjaśnić, dlaczego w tym momencie, ale kiedy dodałem atrybut '[flags]', zaczął zachowywać się zgodnie z oczekiwaniami. Tak jak wspomniany @ Saeb-Amini, Mogłoby być lepiej użyć metody .HasFlag(), ale będąc starą szkołą C++, zwykle wracam do swoich korzeni w kodowaniu bitowym. :) –

3

zbyt długo na komentarz:

Właściwie atrybut [Flags] nie zmienia semantyki enum jest w ogóle, to najbardziej powszechnie stosowane metodą ToString emitować serię nazw zamiast numeru do łącznej wartości .

Załóżmy, że Twój enum został uznany za tak (bez atrybutu Flags):

enum TicketStatus 
{ 
    Created = 1, 
    Transferred = 2, 
    Sold = 4 
} 

Można nadal łączyć różne elementy i wykonywać żadnych działań arytmetycznych, które odnoszą się do Flags ENUM:

TicketStatus status = TicketStatus.Created | TicketStatus.Transferred; 

Jednak poniższe zostaną wydrukowane: 3:

Console.WriteLine(status); 

Ale jeśli dodasz atrybut [Flags], wydrukuje on Created, Transferred.

Ponadto, ważne jest, aby pamiętać, że przez TicketStatus.Created | TicketStatus.Transferred jesteś naprawdę robi bitowego OR na podstawowej wartości całkowitej, zauważyć, jak w naszym przykładzie, że przypisane wartości są jednoznacznie łączyć:

Created : 0001 
Transferred: 0010 
Sold:  0100 

Dlatego wartość z 3 można jednoznacznie określić jako kombinację Created i Transferred.Jednak gdybyśmy mieli to:

enum TicketStatus 
{ 
    Created = 1,  // 0001 
    Transferred = 2, // 0010 
    Sold = 3,   // 0011 
} 

Jak to jest oczywiste, przez reprezentacje binarne, łącząc wartości i sprawdzanie przeciwko członkom jest problematyczne jako połączone elementy mogą być niejednoznaczne. na przykład co to jest status tutaj?

status = TicketStatus.Created | TicketStatus.Transferred; 

Czy Created, Transferred czy jest to naprawdę Sold? Jednak kompilator nie będzie narzekał, jeśli spróbujesz to zrobić, co może prowadzić do trudnych do zlokalizowania błędów, takich jak twoje, gdzie niektóre sprawdzenia nie działają tak, jak tego oczekujesz, więc musisz zadbać o to, aby definicja była na miejscu bitowe mieszanie i porównywanie.

Na powiązana uwaga, ponieważ Twoja wypowiedź if jest naprawdę tylko sprawdzenie, czy bilet ma status Created, niezależnie od ich łączyć z innymi członkami, tutaj jest lepszy sposób, aby sprawdzić, że (.NET> = 4):

status.HasFlag(TicketStatus.Created) 

lub (.NET < 4):

(status & TicketStatus.Created) != 0 

co do dlaczego enum nie działa zgodnie z oczekiwaniami, to prawie na pewno, bo nie jawnie określić jednoznacznie bitowe wartości łączyć do i ts członkowie (zazwyczaj uprawnienia dwóch).

+0

Absolutnie, i tak to rozumiem. I dlatego nie rozumiałem, dlaczego nie działa. Technicznie jesteś na miejscu, ale to, co odkryłem, to nie było tak, kiedy faktycznie działa, dopóki nie dodałem atrybutu '[flags]'. Stąd pytanie. Byłoby interesujące dowiedzieć się, co myślał kompilator/JIT, podczas gdy debugger dawał mi to, o czym wspomniałeś. –

+0

Przyjemne i jasne napisanie przy okazji. :) –

Powiązane problemy