4

ジェネリック型パラメーターを含む構造体の配列 (この場合は辞書エントリ構造体) のサイズを推定したいと考えています。そのためには、構造体のサイズが必要です。

struct Entry
{
   int hash;
   int next;
   TKey key;
   TValue value;
}

この構造体のサイズをバイト単位で取得するにはどうすればよいですか?

編集

使い方Marshal.SizeOfに問題があるようです。構造体の型を渡すと、引数をジェネリック型定義にすることはできないという例外が発生します。

代わりに、インスタンスを取るオーバーロードを呼び出すと、たとえば、両方のジェネリック型引数が値型である場合Marshal.SizeOf(default(Entry))に機能します。一般的な引数が例の場合、この例外がスローされます<int, object>

Dictionary`2+Entry[System.Int32,System.Object]' は、アンマネージ構造としてマーシャリングできません。意味のあるサイズまたはオフセットを計算できません。

4

4 に答える 4

11

ILsizeof命令が必要なものになる可能性があるようです。このsizeof命令は、C#sizeofオペレーターによって裏で使用されますが、IL バージョンでは、何らかの理由で制限が少なくなります。

ECMA CLI 仕様(パーティション III、セクション 4.25) には、この命令の説明がありますsizeof

型のサイズをバイト単位で返します。typeTokジェネリック パラメーター、参照型、または値型にすることができます。

参照型の場合、返されるサイズは、参照値によって参照されるオブジェクトに格納されているデータのサイズではなく、対応する型の参照値のサイズです。

[論理的根拠:値型の定義は、CIL が生成されてから、実行のために読み込まれるまでの間に変更される可能性があります。したがって、型のサイズは、CIL の生成時に常にわかっているわけではありません。このsizeof命令により、CIL コードは、フレームワーク クラス ライブラリを呼び出す必要なく、実行時にサイズを決定できます。計算は、実行時または CIL からネイティブ コードへのコンパイル時に完全に実行できます。sizeofこの型の配列内の各要素が占有する合計サイズを返します。これには、実装が追加することを選択したパディングが含まれます。具体的には、配列要素はsizeofバイトごとに離れています。終了理由

sizeof少し単純なランタイム コード生成を使用して、命令を取得できるはずです。

Console.WriteLine("Entry is " + TypeHelper.SizeOf(typeof(Entry)) + " bytes.");

// ...

public static class TypeHelper
{
    public static int SizeOf<T>(T? obj) where T : struct
    {
        if (obj == null) throw new ArgumentNullException("obj");
        return SizeOf(typeof(T?));
    }

    public static int SizeOf<T>(T obj)
    {
        if (obj == null) throw new ArgumentNullException("obj");
        return SizeOf(obj.GetType());
    }

    public static int SizeOf(Type t)
    {
        if (t == null) throw new ArgumentNullException("t");

        return _cache.GetOrAdd(t, t2 =>
            {
                var dm = new DynamicMethod("$", typeof(int), Type.EmptyTypes);
                ILGenerator il = dm.GetILGenerator();
                il.Emit(OpCodes.Sizeof, t2);
                il.Emit(OpCodes.Ret);

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

    private static readonly ConcurrentDictionary<Type, int>
        _cache = new ConcurrentDictionary<Type, int>();
}
于 2013-05-13T12:54:00.460 に答える
4

おおよそのサイズは、hash( 4 バイト (32 ビット アーキテクチャ)) + next(4 バイト (32 ビット アーキテクチャ)) + TKey(参照型の場合はポインタ (32 ビット アーキテクチャ) の 4 バイト、値型の場合はその値のサイズ) の合計になります。再帰で計算される型)) + TValue( と同じTKey)

また

Marshal.SizeOfメソッドを使用するだけです。

于 2013-05-13T09:54:25.537 に答える
0

を使用することもできますMarshal.ReadIntPtr(type.TypeHandle.Value, 4)。管理対象オブジェクトの基本インスタンス サイズを返します。ランタイム メモリ レイアウトの詳細については、 http://msdn.microsoft.com/en-us/magazine/cc163791.aspxを参照してください。

于 2013-05-13T11:31:13.537 に答える
0

(これを書いた後、LukeH が引用した理論的根拠でアプローチが予期されていることに気付きました)

struct Pin : IDisposable
{
    public GCHandle pinHandle;
    public Pin(object o) { pinHandle = GCHandle.Alloc(o, GCHandleType.Pinned); }

    public void Dispose()
    {
        pinHandle.Free();
    }
}

static class ElementSize<T>
{
    private static int CalcSize(T[] testarray)
    {
      using (Pin p = new Pin(testarray))
        return (int)(Marshal.UnsafeAddrOfPinnedArrayElement(testarray, 1).ToInt64()
                   - Marshal.UnsafeAddrOfPinnedArrayElement(testarray, 0).ToInt64());
    }

    static public readonly int Bytes = CalcSize(new T[2]);
}

小さな配列を固定して破棄する方が、動的コンパイルよりも安価であると確信しています。さらに、ジェネリック クラスの static フィールドは、型ごとに安全なデータを保持するための優れた方法です... . は必要ありませんConcurrentDictionary

于 2014-12-03T01:45:38.090 に答える