Ilgenerator:将默认初始化 System.Nullable 推送到 Stack
Ilgenerator: push default initialized System.Nullable to Stack
我目前正在尝试编写代码,用户可以在其中使用默认值初始化 class 的选定属性(用作 SQL 到 C# 映射项目的一部分)。
以下代码给出了一个 VerificationException,它可能会破坏运行时的稳定性:
private static void InitWithNullable(ILGenerator il, MethodInfo setter, Type objType)
{
var local = il.DeclareLocal(objType);
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, local);
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Ldobj, objType);
il.EmitCall(OpCodes.Callvirt, setter, null);
}
其中一个属性的类型为 System.Nullable<MyEnum>
。知道我做错了什么吗?
InitObj
应该是:
il.Emit(OpCodes.Initobj, objType);
来自MSDN:
The following Emit method overload can use the initobj opcode:
ILGenerator.Emit(OpCode, Type)
而 il.Emit(OpCodes.Ldobj, objType);
可能是错误的...我需要查看整个生成的代码。你需要在某处加载 this(即 ldarg.0
)...
可能:
var local = il.DeclareLocal(objType);
il.Emit(OpCodes.Ldarg, 0); // Used by the OpCodes.callvirt
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, objType);
il.Emit(OpCodes.Ldloc, local.Index);
il.EmitCall(OpCodes.Callvirt, setter, null);
就像在 TryRoslyn 反编译示例中一样。
这是一个工作示例,当返回 DBNULL 时,将单个 属性 设置为加载值或默认值
public void MiniTest()
{
IDbConnection con = Session.Connection;
var method = new DynamicMethod("test", null,
new Type[] { typeof(IDataReader), typeof(MyOrder) });
var il = method.GetILGenerator();
var defaultLabel = il.DefineLabel();
var endLabel = il.DefineLabel();
// generate isdbnull check
il.Emit(OpCodes.Ldarg_0); //push reader
il.Emit(OpCodes.Ldc_I4, 0); //push idx on stack
il.EmitCall(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("IsDBNull"), null);
il.Emit(OpCodes.Brtrue_S, defaultLabel);
//stack now empty
//do the read call and assign the value
il.Emit(OpCodes.Ldarg_1); //push data
il.Emit(OpCodes.Ldarg_0); //push reader
il.Emit(OpCodes.Ldc_I4, 0); //push idx on stack
il.EmitCall(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("GetDateTime"), null); // call r.Get...(idx) and push result on stack
//stack is data, DateTime
var ctor = typeof(DateTime?).GetConstructor(new[] { typeof(DateTime) });
il.Emit(OpCodes.Newobj, ctor); // create the system.nullable and push it on stack
// stack is data, DateTime?
var prop = LinqExt.GetPropertyFast<MyOrder, DateTime?>(x => x.Shipment);
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null); // call d.Property setter
//consumes data + Datetime? value
il.Emit(OpCodes.Br, endLabel);
il.MarkLabel(defaultLabel);
//we start with an empty stack here
il.Emit(OpCodes.Ldarg_1); //push data
var local = il.DeclareLocal(typeof(DateTime?));
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, typeof(DateTime?));
il.Emit(OpCodes.Ldloc, local.LocalIndex);
//stack is now data, new DateTime?()
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null);
//stack empty
il.MarkLabel(endLabel);
il.Emit(OpCodes.Ret);
var action = (Action<IDataReader, MyOrder>)method.CreateDelegate(typeof(Action<IDataReader, MyOrder>));
List<MyOrder> data = new List<MyOrder>();
using (var reader = con.Query(
@"Select o.Shipment
from TestOrder o
where o.Order_Id = :id",
0,
new { id = 1 }))
{
while(reader.Read())
{
MyOrder u = new MyOrder();
action(reader, u);
data.Add(u);
}
}
Assert.AreEqual(1, data.Count);
Assert.IsNull( data[0].Shipment);
}
这应转化为以下操作:
Action<IDataReader, MyOrder> action = (r, o) =>
{
if (r.IsDBNull(0))
{
o.Shipment = null;
}
else
{
o.Shipment = r.GetDateTime(0);
}
};
并进一步简化为仅包括 System.Nullable 的设置为 null:
[TestMethod]
public void MicroTest2()
{
var action = InitWithNull<ExtUser, int?>(x => x.NIntId, "MicroTest2Impl");
ExtUser u = new ExtUser();
u.NIntId = 1;
action( u);
Assert.IsNull(u.NIntId);
}
private static Action<TObject> InitWithNull<TObject, TProperty>(
Expression<Func<TObject,TProperty>> property, string name)
{
var method = new DynamicMethod(name, null,
new Type[] { typeof(TObject) });
var il = method.GetILGenerator();
var prop = LinqExt.GetPropertyFast(property);
//we start with an empty stack here
var local = il.DeclareLocal(typeof(TProperty));
il.Emit(OpCodes.Ldarg_0); //push data
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, typeof(TProperty));
il.Emit(OpCodes.Ldloc, local.LocalIndex);
//stack is now data, new TProperty?()
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null);
//stack empty
il.Emit(OpCodes.Ret);
var action = (Action<TObject>)method.CreateDelegate(typeof(Action<TObject>));
return action;
}
我目前正在尝试编写代码,用户可以在其中使用默认值初始化 class 的选定属性(用作 SQL 到 C# 映射项目的一部分)。 以下代码给出了一个 VerificationException,它可能会破坏运行时的稳定性:
private static void InitWithNullable(ILGenerator il, MethodInfo setter, Type objType)
{
var local = il.DeclareLocal(objType);
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, local);
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Ldobj, objType);
il.EmitCall(OpCodes.Callvirt, setter, null);
}
其中一个属性的类型为 System.Nullable<MyEnum>
。知道我做错了什么吗?
InitObj
应该是:
il.Emit(OpCodes.Initobj, objType);
来自MSDN:
The following Emit method overload can use the initobj opcode:
ILGenerator.Emit(OpCode, Type)
而 il.Emit(OpCodes.Ldobj, objType);
可能是错误的...我需要查看整个生成的代码。你需要在某处加载 this(即 ldarg.0
)...
可能:
var local = il.DeclareLocal(objType);
il.Emit(OpCodes.Ldarg, 0); // Used by the OpCodes.callvirt
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, objType);
il.Emit(OpCodes.Ldloc, local.Index);
il.EmitCall(OpCodes.Callvirt, setter, null);
就像在 TryRoslyn 反编译示例中一样。
这是一个工作示例,当返回 DBNULL 时,将单个 属性 设置为加载值或默认值
public void MiniTest()
{
IDbConnection con = Session.Connection;
var method = new DynamicMethod("test", null,
new Type[] { typeof(IDataReader), typeof(MyOrder) });
var il = method.GetILGenerator();
var defaultLabel = il.DefineLabel();
var endLabel = il.DefineLabel();
// generate isdbnull check
il.Emit(OpCodes.Ldarg_0); //push reader
il.Emit(OpCodes.Ldc_I4, 0); //push idx on stack
il.EmitCall(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("IsDBNull"), null);
il.Emit(OpCodes.Brtrue_S, defaultLabel);
//stack now empty
//do the read call and assign the value
il.Emit(OpCodes.Ldarg_1); //push data
il.Emit(OpCodes.Ldarg_0); //push reader
il.Emit(OpCodes.Ldc_I4, 0); //push idx on stack
il.EmitCall(OpCodes.Callvirt, typeof(IDataRecord).GetMethod("GetDateTime"), null); // call r.Get...(idx) and push result on stack
//stack is data, DateTime
var ctor = typeof(DateTime?).GetConstructor(new[] { typeof(DateTime) });
il.Emit(OpCodes.Newobj, ctor); // create the system.nullable and push it on stack
// stack is data, DateTime?
var prop = LinqExt.GetPropertyFast<MyOrder, DateTime?>(x => x.Shipment);
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null); // call d.Property setter
//consumes data + Datetime? value
il.Emit(OpCodes.Br, endLabel);
il.MarkLabel(defaultLabel);
//we start with an empty stack here
il.Emit(OpCodes.Ldarg_1); //push data
var local = il.DeclareLocal(typeof(DateTime?));
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, typeof(DateTime?));
il.Emit(OpCodes.Ldloc, local.LocalIndex);
//stack is now data, new DateTime?()
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null);
//stack empty
il.MarkLabel(endLabel);
il.Emit(OpCodes.Ret);
var action = (Action<IDataReader, MyOrder>)method.CreateDelegate(typeof(Action<IDataReader, MyOrder>));
List<MyOrder> data = new List<MyOrder>();
using (var reader = con.Query(
@"Select o.Shipment
from TestOrder o
where o.Order_Id = :id",
0,
new { id = 1 }))
{
while(reader.Read())
{
MyOrder u = new MyOrder();
action(reader, u);
data.Add(u);
}
}
Assert.AreEqual(1, data.Count);
Assert.IsNull( data[0].Shipment);
}
这应转化为以下操作:
Action<IDataReader, MyOrder> action = (r, o) =>
{
if (r.IsDBNull(0))
{
o.Shipment = null;
}
else
{
o.Shipment = r.GetDateTime(0);
}
};
并进一步简化为仅包括 System.Nullable 的设置为 null:
[TestMethod]
public void MicroTest2()
{
var action = InitWithNull<ExtUser, int?>(x => x.NIntId, "MicroTest2Impl");
ExtUser u = new ExtUser();
u.NIntId = 1;
action( u);
Assert.IsNull(u.NIntId);
}
private static Action<TObject> InitWithNull<TObject, TProperty>(
Expression<Func<TObject,TProperty>> property, string name)
{
var method = new DynamicMethod(name, null,
new Type[] { typeof(TObject) });
var il = method.GetILGenerator();
var prop = LinqExt.GetPropertyFast(property);
//we start with an empty stack here
var local = il.DeclareLocal(typeof(TProperty));
il.Emit(OpCodes.Ldarg_0); //push data
il.Emit(OpCodes.Ldloca, local);
il.Emit(OpCodes.Initobj, typeof(TProperty));
il.Emit(OpCodes.Ldloc, local.LocalIndex);
//stack is now data, new TProperty?()
il.EmitCall(OpCodes.Callvirt, prop.GetSetMethod(), null);
//stack empty
il.Emit(OpCodes.Ret);
var action = (Action<TObject>)method.CreateDelegate(typeof(Action<TObject>));
return action;
}