通用结构的大小

daj*_*ric 6 c# generics il structure sizeof

我需要找出一个通用结构的大小(我不能像sizeof(T)或使用Marshal.SizeOf(...)0>给我一个错误)

所以我写道:

public static class HelperMethods
{
    static HelperMethods()
    {
        SizeOfType = createSizeOfFunc();
    }

    public static int SizeOf<T>()
    {
        return SizeOfType(typeof(T));
    }

    public static readonly Func<Type, int> SizeOfType = null;

    private static Func<Type, int> createSizeOfFunc()
    {
        var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { typeof(Type) });

        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Sizeof); //needs to be il.Emit(OpCodes.Sizeof, typeof(something))
        il.Emit(OpCodes.Ret);

        var func = (Func<Type, int>)dm.CreateDelegate(typeof(Func<Type, int>));
        return func;
    }
}
Run Code Online (Sandbox Code Playgroud)

一个不同之处是il.Emit(OpCodes.Sizeof)需要一个参数,在创建方法(SizeOfType)时我无法传递它.如何使用IL将堆栈中的参数传递给il.Emit(OpCodes.Sizeof)?(或者是一个不同的解决方案,但我想缓存一个函数(委托)而不是第二个答案中提出的结果)

Mik*_*ray 8

计算大小是一个充满问题的东西,因为你需要知道在使用它的上下文中有什么意义.我假设有一个很好的理由Marshal.SizeOf在参数是泛型结构时抛出,但我不知道它是什么.

有了这个警告,这段代码似乎可以工作,并Marshal.SizeOf为非泛型结构提供类似的结果.它生成一个新的动态方法,通过该类型的IL操作码的大小来获取大小.然后它将缓存结果(因为生成动态方法是一些昂贵的)以供将来使用.

public class A { int x,y,z; }
public struct B { int x,y,z,w,a,b; }
public struct C<T> { Guid g; T b,c,d,e,f; }

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine(IntPtr.Size); // on x86 == 4
        Console.WriteLine(SizeHelper.SizeOf(typeof(C<double>))); // prints 56 on x86
        Console.WriteLine(SizeHelper.SizeOf(typeof(C<int>))); // prints 36 on x86
    }
}

static class SizeHelper
{
    private static Dictionary<Type, int> sizes = new Dictionary<Type, int>();

    public static int SizeOf(Type type)
    {
        int size;
        if (sizes.TryGetValue(type, out size))
        {
            return size;
        }

        size = SizeOfType(type);
        sizes.Add(type, size);
        return size;            
    }

    private static int SizeOfType(Type type)
    {
        var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { });
        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Sizeof, type);
        il.Emit(OpCodes.Ret);
        return (int)dm.Invoke(null, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

据我所知,没有办法让你可以缓存的非泛型委托.该SizeOf操作码需要一个元数据标记.它不会从评估堆栈中获取值.

实际上下面的代码也适用.我不确定为什么Marshal.SizeOf(Type)在类型是通用结构时抛出参数异常但Marshal.SizeOf(Object)不抛出.

    public static int SizeOf<T>() where T : struct
    {
        return Marshal.SizeOf(default(T));
    }
Run Code Online (Sandbox Code Playgroud)


Mar*_*ark 6

上面的思考更进一步,我到达:

public static class TypeSize<T>
{
    public readonly static int Size;

    static TypeSize()
    {
        var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { });
        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Sizeof, typeof(T));
        il.Emit(OpCodes.Ret);
        Size = (int)dm.Invoke(null, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

......我认为这是解决问题的最有效方法.

  • +1 不知道为什么这会被否决。这是 IMO 此处介绍的最有效和最优雅的解决方案。静态构造函数/字段组合为您提供“免费”缓存。我进行了一些健全性检查,没有发现任何明显的缺陷。如果这种方法存在严重缺陷,有人可以发表评论吗? (3认同)
  • `new Type[] { }` 可以替换为 `Array.Empty&lt;Type&gt;` 或 `Type.EmptyTypes` 没那么多,但可能会使解决方案稍微好一点 (2认同)

小智 5

现在,如果足够的话,不安全上下文中的非托管类型有可能执行此操作。

    private unsafe int MySizeOf<T>() where T : unmanaged
    {
        return sizeof(T);
    }
Run Code Online (Sandbox Code Playgroud)