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)?(或者是一个不同的解决方案,但我想缓存一个函数(委托)而不是第二个答案中提出的结果)
计算大小是一个充满问题的东西,因为你需要知道在使用它的上下文中有什么意义.我假设有一个很好的理由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)
上面的思考更进一步,我到达:
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)
......我认为这是解决问题的最有效方法.
小智 5
现在,如果足够的话,不安全上下文中的非托管类型有可能执行此操作。
private unsafe int MySizeOf<T>() where T : unmanaged
{
return sizeof(T);
}
Run Code Online (Sandbox Code Playgroud)