С#: Получение размера переменной типа значения во время выполнения?

Я знаю, что языки, такие как C и C++, позволяют определять размер данных (структуры, массивы, переменные...) во время выполнения с использованием функции sizeof(). Я пробовал это на С# и, по-видимому, он не позволяет помещать переменные в функцию sizeof(), но только типы defintions (float, byte, Int32, uint и т.д.), Как я должен это делать?

Практически я хочу, чтобы это произошло:

int x;
Console.WriteLine(sizeof(x));   // Output: 4

И НЕ:

Console.WriteLine(sizeof(int)); // Output: 4

Я уверен, что есть обычный способ получить размер данных во время выполнения на С#, но google не оказал большой помощи. Вот это моя последняя надежда

Ответ 1

Чтобы найти размер произвольной переменной x, во время выполнения вы можете использовать Marshal.SizeOf:

System.Runtime.InteropServices.Marshal.SizeOf(x)

Как упоминалось в dtb, эта функция возвращает размер переменной после сортировки, но в моем опыте, который обычно является желаемым размером, так как в чистой управляемой среде размер переменной представляет небольшой интерес.

Ответ 2

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

int x = 42;
Console.WriteLine(Utils.SizeOf(x));    // Output: 4

// ...

public static class Utils
{
    public static int SizeOf<T>(T obj)
    {
        return SizeOfCache<T>.SizeOf;
    }

    private static class SizeOfCache<T>
    {
        public static readonly int SizeOf;

        static SizeOfCache()
        {
            var dm = new DynamicMethod("func", typeof(int),
                                       Type.EmptyTypes, typeof(Utils));

            ILGenerator il = dm.GetILGenerator();
            il.Emit(OpCodes.Sizeof, typeof(T));
            il.Emit(OpCodes.Ret);

            var func = (Func<int>)dm.CreateDelegate(typeof(Func<int>));
            SizeOf = func();
        }
    }
}

Ответ 3

Размер int всегда будет 32 бита. Зачем вам нужен размер во время выполнения?

С учетом сказанного вы можете использовать Marshal.SizeOf(), но это действительно предназначено только для неуправляемого кода.

Я наткнулся на некоторый код, который, по-видимому, даст вам размер типа значения. Он использует отражение и будет довольно дорогостоящим вызовом метода по сравнению с функциональностью, которую вы хотели использовать (sizeof()):

using System;
using System.Reflection;
using System.Reflection.Emit;

...

// GetManagedSize() returns the size of a structure whose type
// is 'type', as stored in managed memory. For any referenec type
// this will simply return the size of a pointer (4 or 8).
public static int GetManagedSize(Type type)
{
    // all this just to invoke one opcode with no arguments!
    var method = new DynamicMethod("GetManagedSizeImpl", typeof(uint), new Type[0], typeof(TypeExtensions), false);

    ILGenerator gen = method.GetILGenerator();

    gen.Emit(OpCodes.Sizeof, type);
    gen.Emit(OpCodes.Ret);

    var func = (Func<uint>)method.CreateDelegate(typeof(Func<uint>));
    return checked((int)func());
}

Ответ 4

Я собирался сказать, что нужно использовать тип вывода для удовлетворения вашего требования ("если вы измените тип x из int, чтобы сказать долгое время, вам не нужно заменять каждое появление sizeof (int) на sizeof (long long)"):

public unsafe void GetSizeOf<T>(T exemplar)
    where T : struct
{
    return sizeof(T);
}

Но вы не можете этого сделать, потому что T может быть "управляемым типом" - это может быть структура с полем ссылки объекта. Кажется, что нет способа ограничить T только неуправляемыми типами.

Вы можете использовать статический вспомогательный класс:

public static class Size
{
    public int Of(int x)
    {
        return sizeof(int);
    }

    public int Of(long x)
    {
        return sizeof(long);
    }

    public unsafe int Of(MyStruct x)
    {
        //only works if MyStruct is unmanaged
        return sizeof(MyStruct);
    }
}
public class Program
{
    public void Main()
    {
        int x = 0;
        Console.WriteLine(Size.Of(x));
    }
    public void OldMain()
    {
        long x = 0;
        Console.WriteLine(Size.Of(x));
    }
}

Ответ 5

Если вы делаете что-то вроде создания пакетов данных для отправки на устройство, попробуйте следующее:

byte[] dataBytes = BitConverter.GetBytes(x);
int dataLength= dataBytes.Length;

Теперь вы можете, например, скопировать массив dataBytes в раздел "Полезная нагрузка" массива dataPacket, а dataLength расскажет вам, сколько байтов нужно скопировать, и дайте возможность проверить или установить значение PayloadLength в вашем пакете данных.

Ответ 6

Пошла вперед и добавила некоторые функции безопасности/производительности/удобства в код CORY, опубликованный, для менее параноидального кода LukeH достаточно.

Короче говоря, этот класс возвращает размеры типоразмеров, гарантируя, что кеш используется по мере возможности, завершая исключения из внешних классов по мере их использования.

Возможно, вы захотите переписать блоки блокировки, чтобы лучше соответствовать вашему проекту.

/* A class for finding the sizes of types and variables */
public static class Sizes
{
    /* Retrieves the size of the generic type T
        Returns the size of 'T' on success, 0 otherwise */
    public static int SizeOf<T>()
    {
        return FetchSizeOf(typeof(T));
    }

    /* Retrieves the size of the type of obj
        Returns the size of 'obj' on success, 0 otherwise */
    public static int SizeOf<T>(T obj)
    {
        return FetchSizeOf(typeof(T));
    }

    /* Retrieves the size of 'type'
        Returns the size of 'type' on success, 0 otherwise */
    public static int SizeOf(this Type type)
    {
        return FetchSizeOf(type);
    }

    /* Gets the size of the specified type
        Returns the size of 'type' on success, 0 otherwise*/
    private static int FetchSizeOf(this Type type)
    {
        if ( typeSizeCache == null )
            CreateCache();

        if ( typeSizeCache != null )
        {
            int size = 0;
            if ( GetCachedSizeOf(type, out size) )
                return size;
            else
                return CalcAndCacheSizeOf(type);
        }
        else
            return CalcSizeOf(type);
    }

    /* Attempts to get the size of type from the cache
        Returns true and sets size on success, returns
        false and sets size to 0 otherwise. */
    private static bool GetCachedSizeOf(Type type, out int size)
    {
        size = 0;
        try
        {
            if ( type != null )
            {
                if ( !typeSizeCache.TryGetValue(type, out size) )
                    size = 0;
            }
        }
        catch
        {
            /*  - Documented: ArgumentNullException
                - No critical exceptions. */
            size = 0;
        }
        return size > 0;
    }

    /* Attempts to calculate the size of 'type', and caches
        the size if it is valid (size > 0)
        Returns the calclated size on success, 0 otherwise */
    private static int CalcAndCacheSizeOf(Type type)
    {
        int typeSize = 0;
        try
        {
            typeSize = CalcSizeOf(type);
            if ( typeSize > 0 )
                typeSizeCache.Add(type, typeSize);
        }
        catch
        {
            /*  - Documented: ArgumentException, ArgumentNullException,
                - Additionally Expected: OutOfMemoryException
                - No critical exceptions documented. */
        }
        return typeSize;
    }

    /* Calculates the size of a type using dynamic methods
        Return the type size on success, 0 otherwise */
    private static int CalcSizeOf(this Type type)
    {
        try
        {
            var sizeOfMethod = new DynamicMethod("SizeOf", typeof(int), Type.EmptyTypes);
            var generator = sizeOfMethod.GetILGenerator();
            generator.Emit(OpCodes.Sizeof, type);
            generator.Emit(OpCodes.Ret);

            var sizeFunction = (Func<int>)sizeOfMethod.CreateDelegate(typeof(Func<int>));
            return sizeFunction();
        }
        catch
        {
            /*  - Documented: OutOfMemoryException, ArgumentNullException,
                              ArgumentException, MissingMethodException,
                              MethodAccessException
                - No critical exceptions documented. */
        }
        return 0;
    }

    /* Attempts to allocate the typeSizesCache
        returns whether the cache is allocated*/
    private static bool CreateCache()
    {
        if ( typeSizeCache == null )
        {
            try
            {
                typeSizeCache = new Dictionary<Type, int>();
            }
            catch
            {
                /*  - Documented: OutOfMemoryException
                    - No critical exceptions documented. */
                typeSizeCache = null;
            }
        }
        return typeSizeCache != null;
    }

    /* Static constructor for Sizes, sets typeSizeCache to null */
    static Sizes()
    {
        CreateCache();
    }

    /* Caches the calculated size of various types */
    private static Dictionary<Type, int> typeSizeCache;
}

Ответ 7

public static class TypeSize
{
    public static int GetSize<T>(this T value)
    {
        if (typeof(T).IsArray)
        {
            var elementSize = GetTypeSize(typeof(T).GetElementType());
            var length = (value as Array)?.GetLength(0);
            return length.GetValueOrDefault(0) * elementSize;
        }
        return GetTypeSize(typeof(T));
    }

    static ConcurrentDictionary<Type, int> _cache = new ConcurrentDictionary<Type, int>();

    static int GetTypeSize(Type type)
    {
        return _cache.GetOrAdd(type, _ =>
        {
            var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { });
            ILGenerator il = dm.GetILGenerator();
            il.Emit(OpCodes.Sizeof, _);
            il.Emit(OpCodes.Ret);
            return (int)dm.Invoke(null, null);
        });
    }
}