Rozważać:
using System;
public class Test
{
enum State : sbyte { OK = 0, BUG = -1 }
static void Main(string[] args)
{
var s = new State[1, 1];
s[0, 0] = State.BUG;
State a = s[0, 0];
Console.WriteLine(a == s[0, 0]); // False
}
}
Jak można to wytłumaczyć? Występuje w kompilacjach debugowania w programie Visual Studio 2015 podczas uruchamiania w JIT x86. Wersja kompilacji lub działająca w JIT x64 drukuje True zgodnie z oczekiwaniami.
Aby odtworzyć z wiersza poleceń:
csc Test.cs /platform:x86 /debug
( /debug:pdbonly
, /debug:portable
A /debug:full
także reprodukować.)
ildasm
, a następnieilasm
„naprawia” to .../debug=IMPL
liście flag to połamane;/debug=OPT
„naprawia” to.Odpowiedzi:
Znalazłeś błąd generowania kodu w jitterze .NET 4 x86. Jest to bardzo nietypowe, zawodzi tylko wtedy, gdy kod nie jest zoptymalizowany. Kod maszynowy wygląda następująco:
Trudna sprawa z wieloma elementami tymczasowymi i powielaniem kodu, to normalne w przypadku niezoptymalizowanego kodu. Godna uwagi jest instrukcja pod numerem 013F04B8, czyli miejsce, w którym zachodzi konieczna konwersja z bajtu na 32-bitową liczbę całkowitą. Funkcja pomocnicza pobierająca tablicę zwróciła wartość 0x0000000FF, równą State.BUG, która musi zostać przekonwertowana na -1 (0xFFFFFFFF), zanim wartość będzie mogła zostać porównana. Instrukcja MOVSX jest instrukcją Sign eXtension.
To samo dzieje się ponownie w 013F04CC, ale tym razem nie ma instrukcji MOVSX, która wykonałaby taką samą konwersję. Tam właśnie spadają chipy, instrukcja CMP porównuje 0xFFFFFFFF z 0x000000FF i to jest fałsz. Jest to więc błąd pominięcia, generator kodu nie wyemitował ponownie MOVSX, aby wykonać tę samą konwersję sbyte na int.
Szczególnie niezwykłe w tym błędzie jest to, że działa on poprawnie po włączeniu optymalizatora, teraz wie, że używa MOVSX w obu przypadkach.
Prawdopodobnym powodem, dla którego ten błąd pozostawał niewykryty przez tak długi czas, jest użycie sbyte jako podstawowego typu wyliczenia. Dość rzadkie. Korzystanie z wielowymiarowej tablicy ma również znaczenie instrumentalne, połączenie jest fatalne.
W przeciwnym razie powiedziałbym, że jest to dość krytyczny błąd. Trudno zgadnąć, jak powszechne może to być, do przetestowania mam tylko jitter 4.6.1 x86. Jitter x64 i 3.5 x86 generują bardzo różny kod i pozwalają uniknąć tego błędu. Tymczasowym obejściem, aby kontynuować, jest usunięcie sbyte jako podstawowego typu wyliczenia i pozostawienie go domyślnym, int , więc nie jest potrzebne żadne rozszerzenie znaku.
Możesz zgłosić błąd pod adresem connect.microsoft.com, łącze do tego pytania i odpowiedzi powinno wystarczyć, aby przekazać im wszystko, co powinni wiedzieć. Daj mi znać, jeśli nie chcesz tracić czasu, a ja się tym zajmę.
źródło
byte
zamiast tego równieżsbyte
powinno być w porządku i może być preferowane, jeśli prawdziwy kod jest używany z, powiedzmy, ORM, w którym nie chcesz, aby Twoje flagi w bazie danych zajmowały dodatkowe miejsce.Rozważmy deklarację OP:
Ponieważ błąd występuje tylko wtedy, gdy
BUG
jest ujemny (od -128 do -1), a Stan jest wyliczeniem bajtu ze znakiem, zacząłem przypuszczać, że gdzieś wystąpił problem z rzutowaniem.Jeśli uruchomisz to:
wyświetli:
Z powodu, który ignoruję (na razie)
s[0, 0]
jest rzutowany na bajt przed oceną i dlatego twierdzi, żea == s[0,0]
jest fałszywy.źródło