具有泛型类型字段的struct的大小

时间:2013-05-13 09:51:47

标签: c# .net struct

我想估计包含泛型类型参数的结构数组的大小,在本例中是一个字典条目结构。要做到这一点,我需要结构的大小。

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 个答案:

答案 0 :(得分:9)

听起来IL sizeof指令可能就是您所需要的。 sizeof指令由幕后的C#sizeof运算符使用,但由于某种原因,IL版本的限制较少。

ECMA CLI specification(分区III,第4.25节)对sizeof指令进行了描述:

  

返回类型的大小(以字节为单位)。 typeTok可以是通用的   参数,引用类型或值类型。

     

对于引用类型,返回的大小是引用的大小   相应类型的值,而不是存储的数据的大小   参考值引用的对象。

     

[基本原理:值类型的定义可以在。之间改变   生成CIL的时间和加载时间   执行。因此,当CIL时,类型的大小并不总是已知   生成。 sizeof指令允许CIL代码确定   运行时的大小,无需调用Framework类   图书馆。计算可以完全在运行时或在   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>();
}

答案 1 :(得分:4)

近似大小将是hash(4字节(32位架构))+ next(4字节(32位架构))+ {{1 (如果指针的引用类型为4个字节(32位体系结构),如果值类型为递归计算的该值类型的大小))+ TKey(与TValue相同)

只需使用Marshal.SizeOf方法。

答案 2 :(得分:0)

您也可以使用Marshal.ReadIntPtr(type.TypeHandle.Value, 4)。它返回托管对象的基本实例大小。有关运行时内存布局的详细信息,请参阅http://msdn.microsoft.com/en-us/magazine/cc163791.aspx

答案 3 :(得分: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