通用结构的大小

时间:2013-08-10 23:15:29

标签: c# generics structure sizeof il

我需要找出一个通用结构的大小(我不能像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 个答案:

答案 0 :(得分:7)

计算大小是一个充满问题的东西,因为你需要知道在使用它的上下文中有什么意义。我假设当参数是泛型结构时,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);
    }
}

修改

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

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

    public static int SizeOf<T>() where T : struct
    {
        return Marshal.SizeOf(default(T));
    }

答案 1 :(得分:5)

进一步采取上述思路,我到达了:

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

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

答案 2 :(得分:2)

您的目标似乎是在编译时从函数返回类型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();

答案 3 :(得分:0)

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

    private unsafe int MySizeOf<T>() where T : unmanaged
    {
        return sizeof(T);
    }