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

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

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

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

И

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

Я уверен, что есть нормальный способ получить размер данных во время выполнения в C#, но Google не сильно поможет.. Вот она, моя последняя надежда!--3-->

7 ответов


чтобы найти размер произвольной переменной,x, во время выполнения вы можете использовать Маршал.Оператор sizeof:

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

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


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

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

размер 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());
}

Я собирался сказать использовать вывод типа для удовлетворения вашего требования ("если вы измените тип x из int, чтобы сказать long long, вам не нужно заменять каждое вхождение 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));
    }
}

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

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

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


пошли дальше и добавили некоторые безопасность/качество/удобство на выложили код кори, для кода, тем меньше параноиком LukeH достаточно.

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

вы можете переписать блоки catch-all, чтобы лучше соответствовать вашему проекту.

/* 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's 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;
}

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