web-dev-qa-db-fra.com

C #: Obtenir la taille d'une variable de type valeur lors de l'exécution?

Je sais que des langages tels que C et C++ permettent de déterminer la taille des données (structs, tableaux, variables ...) lors de l'exécution en utilisant la fonction sizeof (). J'ai essayé cela en C # et apparemment cela ne permet pas de mettre des variables dans la fonction sizeof (), mais uniquement des définitions de type (float, byte, Int32, uint, etc ...), comment suis-je censé faire cela?

En pratique, je veux que cela se produise:

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

ET PAS:

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

Je suis sûr qu'il existe un moyen normal d'obtenir la taille des données lors de l'exécution en C #, mais Google n'a pas donné beaucoup d'aide. Voici mon dernier espoir

22
Giora Ron Genender

Pour trouver la taille d'une variable arbitraire, x, lors de l'exécution, vous pouvez utiliser Marshal.SizeOf :

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

Comme mentionné par dtb, cette fonction renvoie la taille de la variable après le marshaling , mais d'après mon expérience, c'est généralement la taille que vous voulez, comme dans un pur environnement géré la taille d'une variable est de peu d'intérêt.

25
Grokys

Dans la continuité de réponse de Cory , si les performances sont importantes et que vous devez souvent frapper ce code, vous pouvez mettre en cache la taille afin que la méthode dynamique ne soit créée et exécutée qu'une seule fois par type:

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

La taille de int sera toujours de 32 bits. Pourquoi auriez-vous besoin d'obtenir la taille lors de l'exécution?

Cela dit, vous pourriez utiliser Marshal.SizeOf() , mais cela est vraiment destiné à non géré code uniquement.

I tombé sur du code qui vous donnera apparemment la taille d'un type de valeur. Il utilise la réflexion et serait un appel de méthode assez coûteux par rapport à la fonctionnalité que vous vouliez utiliser (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());
}
15
Cᴏʀʏ

Si vous faites quelque chose comme construire des paquets de données à envoyer à un appareil, essayez ceci:

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

Vous pouvez maintenant, par exemple, copier le tableau dataBytes dans la section Payload du tableau dataPacket, et dataLength vous indiquera le nombre d'octets à copier et vous permettra de valider ou de définir la valeur PayloadLength dans votre paquet de données.

4
XKCD137
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[0]);
            ILGenerator il = dm.GetILGenerator();
            il.Emit(OpCodes.Sizeof, type);
            il.Emit(OpCodes.Ret);
            return (int)dm.Invoke(null, null);
        });
    }
}
4
Mark

J'allais dire utiliser l'inférence de type pour répondre à vos besoins ("si vous changez le type de x de int pour dire long long, vous n'avez pas à remplacer chaque occurrence de sizeof (int) par sizeof (long long)") :

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

Mais vous ne pouvez pas faire cela, car T pourrait être un "type géré" - il pourrait s'agir d'une structure avec un champ de référence d'objet. Il ne semble pas y avoir de moyen de limiter T à des types non gérés uniquement.

Vous pouvez utiliser une classe d'assistance statique:

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

Nous sommes allés de l'avant et avons ajouté quelques fonctionnalités de sécurité/performances/commodité au code CORY publié, car le code de LukeH moins paranoïaque devrait suffire.

En bref, cette classe renvoie des tailles de type, garantissant que le cache est utilisé dans la mesure du possible, enveloppant les exceptions des classes externes au fur et à mesure.

Vous voudrez peut-être réécrire les blocs fourre-tout pour mieux convenir à votre projet.

/* 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
user5369700