我需要找出一个通用结构的大小(我不能像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)?在第二个答案)
答案 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);
}