我目前正在尝试编写代码,用户可以使用默认值初始化类的选定属性(用作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>
类型。我有什么想法吗?
答案 0 :(得分:0)
InitObj
应为:
il.Emit(OpCodes.Initobj, objType);
来自MSDN:
以下Emit方法重载可以使用initobj操作码:
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反编译样本一样。
答案 1 :(得分:0)
以下是在返回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;
}