7

我需要找出通用结构的大小(我不能像 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;
    }
}

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

4

4 回答 4

11

将上述想法更进一步,我得出:

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);
    }
}

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

于 2017-02-24T11:28:01.230 回答
9

计算大小充满了问题,因为您需要知道在使用它的上下文中什么是有意义的。我认为Marshal.SizeOf当参数是通用结构时有充分的理由抛出,但我不知道它是什么。

有了这个警告,这段代码似乎可以工作,并给出与Marshal.SizeOf非泛型结构类似的结果。它生成一个新的动态方法,该方法通过 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);
    }
}

编辑

据我所知,没有办法制作可以缓存的非通用委托。SizeOf操作码需要元数据令牌。它不从评估堆栈中获取值。

实际上下面的代码也可以。我不确定当类型是泛型结构但不是时为什么Marshal.SizeOf(Type)会引发参数异常。Marshal.SizeOf(Object)

    public static int SizeOf<T>() where T : struct
    {
        return Marshal.SizeOf(default(T));
    }
于 2013-08-11T00:22:37.170 回答
4

看来您的目标是在编译时Type从函数返回类型中解析参数。Func<Type, int>此信息在编译时是未知的,并且没有明显的方法可以在运行时使用反射来解析此信息。

我看不出返回动态方法有什么好处,而不是调用动态方法并立即返回结果。鉴于您没有给出任何上下文,我会提出明显的解决方案,立即返回该值。如果您关心的是性能,那么只需将结果缓存在字典中。

public static class GlobalExtensions
{

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

    public static int SizeOf(this Type type)
    {
        var dynamicMethod = new DynamicMethod("SizeOf", typeof(int), Type.EmptyTypes);
        var generator = dynamicMethod.GetILGenerator();

        generator.Emit(OpCodes.Sizeof, type);
        generator.Emit(OpCodes.Ret);

        var function = (Func<int>) dynamicMethod.CreateDelegate(typeof(Func<int>));
        return function();
    }
}

使用扩展方法利用了一些不错的语法。您现在可以使用以下代码继续获取通用结构或类的大小:

var size = TypeExtensions.SizeOf<Example<int>>();

//alternative syntax... 
size = typeof (Example<int>).SizeOf();
于 2013-08-11T00:27:40.833 回答
4

现在,如果足够的话,不安全上下文中的非托管类型就有可能这样做。

    private unsafe int MySizeOf<T>() where T : unmanaged
    {
        return sizeof(T);
    }
于 2018-12-29T18:54:41.013 回答