2017-12-21 147 views
30

Przede wszystkim mały disclaimer przed właściwym pytaniem:Jaka jest różnica między sizeof (T) i Unsafe.SizeOf <T>()?

Wiem, że istnieje wiele zamkniętych/powielać pytania dotyczące różnicy pomiędzy operatorem sizeof i sposobu Marshal.SizeOf<T> i rozumiem różnicę między dwójka. Tutaj mówię o metodzie SizeOf<T> w nowym Unsafe klasy

tak, to nie jestem pewien, czy rozumiem rzeczywistą różnicę między tymi dwoma procesami, a także czy istnieje specyficzna różnica przy użyciu metody na struct/klasa w szczególności.

Operator sizeof bierze nazwę Type i zwraca liczbę bajtówzarządzany jest podobno zająć kiedy przydzielane (tj. Int32 powróci 4, na przykład).

Sposób Unsafe.SizeOf<T> z drugiej strony, jest realizowany w IL jak wszystkich innych metod w klasie Unsafe, a patrząc na kod oto co robi:

.method public hidebysig static int32 SizeOf<T>() cil managed aggressiveinlining 
{ 
    .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor() = (01 00 00 00) 
    .maxstack 1 
    sizeof !!T 
    ret 
} 

Teraz, jeśli nie jestem źle, kod wywołuje tylko sizeof !!T, który jest taki sam jak sizeof(T) (wywołanie operatora sizeof o nazwie typu T), więc czy oba nie byłyby dokładnie równoważne?

Widzę również, że metoda polega również na przydzielaniu bezużytecznego obiektu (NonVersionableAttribute) w pierwszym wierszu, więc czy nie spowoduje to, że niewielka ilość pamięci zostanie również przydzielona do sterty?

Moje pytanie brzmi:

Czy można bezpiecznie powiedzieć, że te dwie metody są całkowicie równoważne i że w związku z tym jest to po prostu lepiej użyć klasycznego operatora sizeof, jak również uniknąć podziału tego atrybutu w metoda SizeOf<T>? Czy ta metoda została dodana do klasy SizeOf<T> do klasy Unsafe tylko dla wygody?

+1

Opcja '.custom' oświadczenie tylko mówi, że tam jest obecny niestandardowy atrybut przynależności do tej metody. Ponieważ atrybuty niestandardowe są po prostu metadanymi, nie biorą udziału w zwykłym wywołaniu metody. – thehennyy

+0

Po prostu wyjaśnienie, czy jest to klasa BCL, nie jestem świadomy, lub niektóre [rozszerzenie strony trzeciej] (https://github.com/DotNetCross/Memory.Unsafe)? Masz rację, że nic nie robi, być może chodziło o zapewnienie "haka" dla niektórych platformowych magii w przyszłości? – Groo

+2

@Groo Jest to nowa klasa, która została dodana do CoreFX Wierzę w .NET Core 2.0 (nie do końca pewny, ale tak czy owak jest w .NET Core 2.0). Zobacz tutaj: https://docs.microsoft.com/it-it/dotnet/api/system.runtime.compilerservices.unsafe?view=netcore-2.0 – Sergio0694

Odpowiedz

21

Chociaż metoda ta rzeczywiście tylko wykorzystuje sizeof instrukcji IL - istnieje różnica regularne operatora sizeof, ponieważ operator nie może być stosowany do dowolnych typów:

stosowane do uzyskania rozmiar w bajtach dla typ niezarządzalny.Niezarządzane typy obejmują wbudowanych typów, które są wymienione w tabeli poniżej, a także następujących: Rodzaje

Enum

typów Pointer

elemencie zdefiniowane przez użytkownika, które nie zawierają żadnych pola lub właściwości, które są typy referencyjne

Jeśli spróbujesz napisać analogu Unsafe.SizeOf - to nie będzie działać:

public static int SizeOf<T>() 
{ 
    // nope, will not compile 
    return sizeof(T); 
} 

Więc Unsafe.SizeOf wyciągi ograniczenia sizeof operatora i pozwala na używanie IL sizeof dyspozycję z dowolnych typów (w tym typów referencyjnych, dla których zwróci wielkość odniesienia).

Jeśli chodzi o konstrukcje atrybutów, które widzisz w IL - nie oznacza to, że dla każdego wywołania zostanie utworzony atrybut - to tylko składnia IL dla powiązania atrybutów z różnymi elementami (w tym przypadku metoda).

Przykłady:

public struct Test { 
    public int Int1; 
} 

static void Main() { 
    // works 
    var s1 = Unsafe.SizeOf<Test>(); 
    // doesn't work, need to mark method with "unsafe" 
    var s2 = sizeof(Test);    
} 

Inny przykład:

public struct Test { 
    public int Int1; 
    public string String1; 
} 


static unsafe void Main() { 
    // works, return 16 in 64bit process - 4 for int, 4 for padding, because 
    // alignment of the type is the size of its largest element, which is 8 
    // and 8 for string 
    var s1 = Unsafe.SizeOf<Test>(); 
    // doesn't work even with unsafe, 
    // cannot take size of variable of managed type "Test" 
    // because Test contains field of reference type (string) 
    var s2 = sizeof(Test);       
} 
+0

Idealne, dzięki! To trochę dziwne, że potrzebujemy tej metody, aby móc to zrobić, zastanawiam się, dlaczego zdecydowali się nie aktualizować kompilatora C#, aby operator 'sizeof' zrobił to samo bez tej dodatkowej metody IL. Co więcej, poczekaj chwilę, czy 'int' (jako struktura' Int32') nie powinien być zawsze 4-bajtowy, a nawet 64-bitowy? Czy rozmiar "Testu" nie powinien wynosić 12 bajtów? – Sergio0694

+1

@ Sergio0694 yes sure, int to 4 bajty, a kolejne 4 bajty to dopełnienie, naprawione. Jeśli chodzi o to, dlaczego nie zmienić operatora sizeof - nie wiem dokładnie, oczywiście, ale myślę, że to wymaga zmiany w kompilatorze i 'Unsafe.SizeOf' nie wymaga żadnych zmian. Plus, był powód przede wszystkim do tego, aby 'sizeof' działało w ten sposób, więc przypuszczam, że musisz naprawdę wiedzieć, co robisz, kiedy używasz' Unsafe.SizeOf' (w końcu jest to niebezpieczne). – Evk

+0

Ma sens, dzięki za dodatkowe wyjaśnienie! Całkowicie zapomniałem o wyrównaniu przesunięcia pola, minęło trochę czasu odkąd ostatnio kodowałem w C i często zapominasz o tych drobnych szczegółach podczas używania zarządzanego języka ahahahah – Sergio0694