C #: получение размера переменной типа значения во время выполнения? - PullRequest
21 голосов
/ 17 ноября 2011

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

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

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

И НЕ:

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

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

Ответы [ 7 ]

23 голосов
/ 17 ноября 2011

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

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

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

22 голосов
/ 18 ноября 2011

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

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();
        }
    }
}
15 голосов
/ 17 ноября 2011

Размер 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 голосов
/ 24 февраля 2017
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);
        });
    }
}
4 голосов
/ 28 марта 2014

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

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

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

3 голосов
/ 24 сентября 2015

Забегая вперед и добавив некоторые функции безопасности / производительности / удобства в код, опубликованный CORY, для менее параноидального кода 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;
}
3 голосов
/ 18 ноября 2011

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