Подтвердить что ты не робот

Какая разница между sizeof (T) и Unsafe.SizeOf <T>()?

Прежде всего, небольшая оговорка перед актуальным вопросом:

Я знаю, что существует много закрытых/повторяющихся вопросов относительно разницы между оператором sizeof и Marshal.SizeOf<T>, и я понимаю разницу между ними. Здесь я говорю о методе SizeOf<T> в новом классе Unsafe

Итак, я не уверен, что понимаю реальную разницу между этими двумя операциями и имеет ли конкретное различие при использовании метода в struct/class в частности.

Оператор sizeof принимает имя Тип и возвращает число управляемых байтов, которое предполагается использовать при распределении (т.е. Int32 вернется 4, например).

Метод Unsafe.SizeOf<T>, с другой стороны, реализован в IL, как и все другие методы класса Unsafe, и просматривая здесь код:

.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
}

Теперь, если я не ошибаюсь, код просто вызывает sizeof !!T, который является таким же, как sizeof(T) (вызывает оператор sizeof с именем типа T), так что бы не два из них в точности эквивалентны?

Кроме того, я вижу, что метод также выделяет бесполезный объект (NonVersionableAttribute) в первой строке, так что это не будет причиной выделения небольшой части памяти?

Мой вопрос:

Можно ли сказать, что эти два метода абсолютно эквивалентны, и поэтому лучше использовать классический оператор sizeof, так как это также позволяет избежать выделения этого атрибута в методе SizeOf<T>? Был ли этот метод SizeOf<T> добавлен в класс Unsafe только для удобства в этот момент?

4b9b3361

Ответ 1

Хотя этот метод действительно использует инструкцию sizeof IL - существует разница с регулярным оператором sizeof, потому что этот оператор нельзя применять к произвольным типам:

Используется для получения размера в байтах для неуправляемого типа. Неуправляемый типы включают встроенные типы, перечисленные в таблице, которые следует, а также следующее:

Типы перечислений

Типы указателей

Определяемые пользователем структуры, которые не содержат поля или свойства, которые являются ссылочными типами

Если вы попытаетесь написать аналог Unsafe.SizeOf - он не будет работать:

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

Итак, Unsafe.SizeOf отменяет ограничения оператора sizeof и позволяет вам использовать инструкцию IL sizeof с произвольными типами (включая ссылочные типы, для которых он будет возвращать размер ссылки).

Как для конструкции атрибута, вы видите в IL - это не означает, что для каждого вызова будет создан экземпляр атрибута - это просто синтаксис IL для связывания атрибутов с различными членами (в этом случае метод).

Примеры:

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);            
}

Другой пример:

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);                        
}