2016-08-09 9 views
5

Gdybym zmienić wartość bool.TrueString, zrobiłbym to za pomocą refleksji:Dlaczego nie można zmienić Type.Delimiter za pomocą Reflection?

typeof(bool).GetField("TrueString", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Yes"); 
Console.WriteLine(bool.TrueString); // Outputs "Yes" 

Jednak nie uda się zmienić wartość, powiedzmy, Type.Delimiter:

typeof(Type).GetField("Delimiter", BindingFlags.Public | BindingFlags.Static).SetValue(null, '-'); 
Console.WriteLine(Type.Delimiter); // Outputs "." 

Dlaczego czy to jest?

+1

Nie wiem, wydaje się strasznym pomysłem. – itsme86

+0

patrząc na źródło, dwa pola są zadeklarowane prawie tak samo, ale nie całkiem, jeden jest tylko do odczytu = ".", Drugi jest tylko do odczytu = inny literał tylko do odczytu. – pm100

+0

Dziki domysł.Ponieważ bool jest strukturą 'struct', a' Type' jest klasą abstrakcyjną, Reflection może wykonać czarną magię na tej strukturze. Jak już powiedziałem .. tylko zgadywanie – lokusking

Odpowiedz

4

Myślę, że padłeś ofiarą optymalizacji, która jest wykonywana przez JIT. W rzeczywistości można zmienić wartość tego pola, ale z jakiegoś powodu wyniki tej zmiany nie będą od razu widoczne. Udało mi się ominąć, że robi coś głupiego:

typeof(Type).GetField("Delimiter", BindingFlags.Public | BindingFlags.Static).SetValue(null, '-'); 
Func<char> getDelimiter =() => Type.Delimiter; 
Console.WriteLine(getDelimiter()); 

Ten kod wiarygodnie pokazał zaktualizowaną wartość pola dla mnie. Nie mogę powiedzieć, że jestem strasznie zaskoczony; pole jest zadeklarowane jako tylko do odczytu, więc JITter może użyć tego założenia przy dostępie do pola. Robisz coś niegrzecznego i złego, nie powinno być żadnych oczekiwań, aby działało to w rozsądny sposób.

Teraz, dlaczego ten nie pojawił się podczas modyfikowania pola bool.TrueString, mój najlepszy Domyślam się, że to ze względu na bool.TrueString będąc rodzajem odniesienia (string) natomiast Type.Delimiter jest typ wartości (char). Mogę sobie wyobrazić, że to powoduje różne optymalizacje.

zrobiłem spojrzeć na demontażu dla tego kodu:

 Console.WriteLine(bool.TrueString); 
006F2E53 8B 0D B8 10 40 03 mov   ecx,dword ptr ds:[34010B8h] 
006F2E59 E8 52 A6 77 54  call  54E6D4B0 

     Console.WriteLine(Type.Delimiter); 
006F2E5E B9 2E 00 00 00  mov   ecx,2Eh 
006F2E63 E8 B0 FA E0 54  call  55502918 

Widać dość wyraźnie, że jitter zoptymalizowany dala dostępu Type.Delimiter pola, zastępując go z wartością dosłownym '.'. Dostęp do pola statycznego dla bool.TrueString nadal wydaje się być ładowany z faktycznego pola.

+1

To dlatego, że jitting tego kodu wykonuje statyczne ctor, a następnie spala Delimiter do kodu x86. Wersja delegata nie odwołuje się do ogranicznika z tej metody. Delimiter jest nadal wypalany w kodzie x86, ale w innej metodzie w późniejszym czasie. – usr

+0

@usr Faktycznie zauważyłem zmianę pola, testując kod w LINQPad, co skłoniło mnie do spróbowania utworzenia delegata, aby spróbować zmusić jita do zobaczenia wartości pola po zmianie. Szczerze mówiąc, to było trochę ujęcie w ciemności z mojej strony, ale udało mi się. – Kyle

1

Różnica polega na tym, że bool.TrueString jest typem odniesienia, a Type.Delimiter jest typem wartości. Spróbuj tego samego kodu przed tymi dwoma właściwościami, a zobaczysz ten sam problem:

public class A 
{ 
    public static readonly string S = "S"; 
    public static readonly char C = 'C'; 
} 

Powodem takiej sytuacji jest fakt, że po raz pierwszy metoda nazywa, dane kompilatora JIT czy wartość static readonly można piec prosto do kodu zespołu jako stała. Jeśli tak, to robi.

Jeśli wydobyć się kod, który uzyskuje dostęp do boiska i innej metody, to jego produkcja będzie zależeć od tego, czy jej pierwszy bieg był przed lub po wartość polu została zmieniona. Ponieważ twój kod jest w tej samej metodzie, to był wyraźnie JITted przed zmianą wartości pola.

Przekonasz się, że jeśli użyjesz typu wartości, który może być zakodowany na stałe w instrukcje dotyczące procesora, ta optymalizacja JIT zostanie zastosowana. Obejmuje to takie rzeczy jak int, long i char, ale nie takie rzeczy jak DateTime s lub typów referencyjnych, takich jak ciągi

Powiązane problemy