4

我想估计包含泛型类型参数的结构数组的大小,在这种情况下是字典条目结构。为此,我需要结构的大小。

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

我怎样才能得到这个结构的字节大小?

编辑

似乎使用Marshal.SizeOf是有问题的。传递结构的类型将引发异常,指出参数不能是泛型类型定义。

如果我改为调用采用实例的重载,例如,如果两个泛型类型参数都是值类型Marshal.SizeOf(default(Entry)),它将起作用。如果泛型参数是 eg则抛出此异常<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

近似大小将是(hash4 字节(32 位架构))+ next(4 字节(32 位架构))+ TKey(如果指针的引用类型为 4 字节(32 位架构),如果值类型为该值的大小递归计算的类型)) + 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]);
}

我相当确定固定和丢弃一个小数组比动态编译便宜。另外,泛型类中的静态字段是拥有类型安全的每种类型数据的好方法……不需要ConcurrentDictionary.

于 2014-12-03T01:45:38.090 回答