属性 字段上的 ILGenerator 调用方法
ILGenerator call method on field from property
我正在尝试在 ILGenerator 中编写一些有助于延迟加载的代码。我遇到问题的部分是在使用 TypeBuilder 构建的 class 中的私有字段上找到的加载方法。
我想在 IL 中完成以下工作
class Proxy
: BaseType
{
private DbContextAccessor _accessor;
private Status _Status;
public override Status Status
{
get
{
if (_Status.IsNull())
{
PropertyInfo info = GetType().GetProperty("Status");
_Status = _accessor.Load<BaseType, Status>(this, info);
}
return _Status;
}
set
{
_Status = value;
}
}
}
以下是我目前写的ILGenerator代码
private void BuildOverriddenProperty(string propertyName, Type propertyType)
{
FieldBuilder propertyField = typeBuilder.DefineField($"_{propertyName}", propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
propertyName,
PropertyAttributes.None,
propertyType,
Type.EmptyTypes);
MethodAttributes methodAttributesForGetAndSet = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual ;
MethodBuilder
getMethod = typeBuilder.DefineMethod($"get_{propertyName}", methodAttributesForGetAndSet, propertyType, Type.EmptyTypes),
setMethod = typeBuilder.DefineMethod($"set_{propertyName}", methodAttributesForGetAndSet, null, new Type[] { propertyType });
ILGenerator
iLGetGenerator = getMethod.GetILGenerator(),
iLSetGenerator = setMethod.GetILGenerator();
Type internalExt = typeof(InternalExtensions);
MethodInfo
load = fieldDbContextAccessor.FieldType.GetMethods()
.Where(method =>
method.Name == (propertyType.IsClass ? "Load" : "LoadCollection"))
.Single().MakeGenericMethod(baseType),
isNull = internalExt.GetMethod("IsNull", BindingFlags.Public | BindingFlags.Static , null, new[] { typeof(object) }, null);
Label fieldIsNotNull = iLGetGenerator.DefineLabel();
LocalBuilder
propertyInfo = iLGetGenerator.DeclareLocal(typeof(PropertyInfo));
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, propertyField); // propertyField
iLGetGenerator.EmitCall(OpCodes.Call, isNull, null); // use the static extension method IsNull
iLGetGenerator.Emit(OpCodes.Brfalse_S, fieldIsNotNull); // value is not null
//{
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.EmitCall(OpCodes.Call, typeof(object).GetMethod("GetType"), null); // call GetType method
iLGetGenerator.Emit(OpCodes.Ldstr, propertyName); // push new string of propertyName
iLGetGenerator.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetProperty", new[] { typeof(string) }), null); // call GetProperty with the propertyName as the parameter
iLGetGenerator.Emit(OpCodes.Stloc, propertyInfo); // store PropertyInfo object in the local variable propertyInfo
// -> this is the problem area that results in invalid program code
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, fieldDbContextAccessor); // field variable _accessor
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this ptr as the first parameter // <- my hunch is this is the problem but uncertain
iLGetGenerator.Emit(OpCodes.Ldloc, propertyInfo); // local variable propertyInfo as the second parameter
iLGetGenerator.EmitCall(OpCodes.Call, load, null); // call the Load or LoadCollection on the DBContextAccessor object
iLGetGenerator.Emit(OpCodes.Stfld, propertyField); // store the return in the propertyField
// -> end
//}
iLGetGenerator.MarkLabel(fieldIsNotNull); // jump here when propertyField is not null
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, propertyField); // propertyField
iLGetGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(getMethod, baseType.GetProperty(propertyName).GetMethod);
iLSetGenerator.Emit(OpCodes.Ldarg_0);
iLSetGenerator.Emit(OpCodes.Ldarg_1);
iLSetGenerator.Emit(OpCodes.Stfld, propertyField);
iLSetGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(setMethod, baseType.GetProperty(propertyName).SetMethod);
}
作业有点棘手。
你必须把这个指针放在最前面,Ldarg_0,然后是你要存储的值,然后是你要存储到的字段。
这是因为你必须将它想象成像 StoreField = Assign(this, value)。
1:LdArg0, So you put the this from PropertyField on the stack
2:LdArg0, you put the ThisPointer from _accessor on the stack
3:LdFld accessor, you load the accessor, remove one value from stack from line (2)
4:LdArg0, you load the this pointer, this time your explicit parameter
5:LdLoc propertyInfo, you load your third parameter
6:Call load, // this will take 3 values from stack, lines (3)-(5)
7;Stfld propertyfield, // you store the property, on stack is the this pointer line(1) and the output of load line(6)
因此您可能需要在评论行正下方添加另一个 LdArg0。-->this
如果你想让你的代码更快,但可读性较差,你可以用 "Dup" 替换 line2,这应该会快一点,并且只是复制堆栈的顶部,这意味着它将再次使用相同的值。
我正在尝试在 ILGenerator 中编写一些有助于延迟加载的代码。我遇到问题的部分是在使用 TypeBuilder 构建的 class 中的私有字段上找到的加载方法。
我想在 IL 中完成以下工作
class Proxy
: BaseType
{
private DbContextAccessor _accessor;
private Status _Status;
public override Status Status
{
get
{
if (_Status.IsNull())
{
PropertyInfo info = GetType().GetProperty("Status");
_Status = _accessor.Load<BaseType, Status>(this, info);
}
return _Status;
}
set
{
_Status = value;
}
}
}
以下是我目前写的ILGenerator代码
private void BuildOverriddenProperty(string propertyName, Type propertyType)
{
FieldBuilder propertyField = typeBuilder.DefineField($"_{propertyName}", propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
propertyName,
PropertyAttributes.None,
propertyType,
Type.EmptyTypes);
MethodAttributes methodAttributesForGetAndSet = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual ;
MethodBuilder
getMethod = typeBuilder.DefineMethod($"get_{propertyName}", methodAttributesForGetAndSet, propertyType, Type.EmptyTypes),
setMethod = typeBuilder.DefineMethod($"set_{propertyName}", methodAttributesForGetAndSet, null, new Type[] { propertyType });
ILGenerator
iLGetGenerator = getMethod.GetILGenerator(),
iLSetGenerator = setMethod.GetILGenerator();
Type internalExt = typeof(InternalExtensions);
MethodInfo
load = fieldDbContextAccessor.FieldType.GetMethods()
.Where(method =>
method.Name == (propertyType.IsClass ? "Load" : "LoadCollection"))
.Single().MakeGenericMethod(baseType),
isNull = internalExt.GetMethod("IsNull", BindingFlags.Public | BindingFlags.Static , null, new[] { typeof(object) }, null);
Label fieldIsNotNull = iLGetGenerator.DefineLabel();
LocalBuilder
propertyInfo = iLGetGenerator.DeclareLocal(typeof(PropertyInfo));
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, propertyField); // propertyField
iLGetGenerator.EmitCall(OpCodes.Call, isNull, null); // use the static extension method IsNull
iLGetGenerator.Emit(OpCodes.Brfalse_S, fieldIsNotNull); // value is not null
//{
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.EmitCall(OpCodes.Call, typeof(object).GetMethod("GetType"), null); // call GetType method
iLGetGenerator.Emit(OpCodes.Ldstr, propertyName); // push new string of propertyName
iLGetGenerator.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetProperty", new[] { typeof(string) }), null); // call GetProperty with the propertyName as the parameter
iLGetGenerator.Emit(OpCodes.Stloc, propertyInfo); // store PropertyInfo object in the local variable propertyInfo
// -> this is the problem area that results in invalid program code
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, fieldDbContextAccessor); // field variable _accessor
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this ptr as the first parameter // <- my hunch is this is the problem but uncertain
iLGetGenerator.Emit(OpCodes.Ldloc, propertyInfo); // local variable propertyInfo as the second parameter
iLGetGenerator.EmitCall(OpCodes.Call, load, null); // call the Load or LoadCollection on the DBContextAccessor object
iLGetGenerator.Emit(OpCodes.Stfld, propertyField); // store the return in the propertyField
// -> end
//}
iLGetGenerator.MarkLabel(fieldIsNotNull); // jump here when propertyField is not null
iLGetGenerator.Emit(OpCodes.Ldarg_0); // this
iLGetGenerator.Emit(OpCodes.Ldfld, propertyField); // propertyField
iLGetGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(getMethod, baseType.GetProperty(propertyName).GetMethod);
iLSetGenerator.Emit(OpCodes.Ldarg_0);
iLSetGenerator.Emit(OpCodes.Ldarg_1);
iLSetGenerator.Emit(OpCodes.Stfld, propertyField);
iLSetGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(setMethod, baseType.GetProperty(propertyName).SetMethod);
}
作业有点棘手。
你必须把这个指针放在最前面,Ldarg_0,然后是你要存储的值,然后是你要存储到的字段。
这是因为你必须将它想象成像 StoreField = Assign(this, value)。
1:LdArg0, So you put the this from PropertyField on the stack
2:LdArg0, you put the ThisPointer from _accessor on the stack
3:LdFld accessor, you load the accessor, remove one value from stack from line (2)
4:LdArg0, you load the this pointer, this time your explicit parameter
5:LdLoc propertyInfo, you load your third parameter
6:Call load, // this will take 3 values from stack, lines (3)-(5)
7;Stfld propertyfield, // you store the property, on stack is the this pointer line(1) and the output of load line(6)
因此您可能需要在评论行正下方添加另一个 LdArg0。-->this
如果你想让你的代码更快,但可读性较差,你可以用 "Dup" 替换 line2,这应该会快一点,并且只是复制堆栈的顶部,这意味着它将再次使用相同的值。