web-dev-qa-db-ja.com

C#:実行時に値型変数のサイズを取得していますか?

CやC++などの言語では、実行時にsizeof()関数を使用してデータ(構造体、配列、変数...)のサイズを決定できることを知っています。私はC#でそれを試してみましたが、どうやら変数をsizeof()関数に入れることはできませんが、型の定義のみ(float、byte、Int32、uintなど...)、どうすればよいですか?

実際には、私はこれを実現させたいです:

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

AND NOT:

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

C#で実行時にデータのサイズを取得する通常の方法があると確信していますが、Googleはあまり役に立ちませんでした。ここが最後の希望です

22

実行時に任意の変数xのサイズを見つけるには、 Marshal.SizeOf を使用できます。

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

Dtbで述べたように、この関数はマーシャリング後の変数のサイズを返しますが、私の経験では、通常、純粋な管理された環境では、変数のサイズはあまり重要ではありません。

25
Grokys

Cory's answer に続いて、パフォーマンスが重要であり、このコードを頻繁に実行する必要がある場合は、サイズをキャッシュして、動的メソッドをタイプごとに1回だけ構築および実行する必要があるようにします。

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

intのサイズは常に32ビットになります。実行時にサイズを取得する必要があるのはなぜですか?

そうは言っても、あなたは-- Marshal.SizeOf() を使用することができますが、実際にはunmanagedコードのみ。

I コードにぶつかった 値型のサイズを明らかに示します。これはリフレクションを使用しており、使用したい機能(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ᴏʀʏ

デバイスに送信するデータパケットを構築するような場合は、次のことを試してください。

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

これで、たとえば、dataBytes配列をdataPacket配列のPayloadセクションにコピーできます。dataLengthは、コピーするバイト数を通知し、データパケットのPayloadLength値を検証または設定できるようにします。

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

私はあなたの要件を満たすためにタイプ推論を使用するつもりでした(「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));
    }
}
3
phoog

より控えめなLukeHのコードで十分なはずなので、先に進んで、CORYが投稿したコードにいくつかの安全/パフォーマンス/便利な機能を追加しました。

つまり、このクラスは型サイズを返し、可能な場合は常にキャッシュが使用されるようにし、外部クラスからの例外を処理します。

プロジェクトにより適するように、キャッチオールブロックを書き直したい場合があります。

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