为了扩展标题,我正在使用Reflection生成一个自定义子类,该子类最终将具有任意数量的字符串属性,它们将在内部存储在字典中。在这种情况下,我只使用一个。我已经使用Ildasm.exe来获取所需的MSIL我的Set方法有效,因为调试器显示了我分配的值,但是当我尝试读回它时,我收到InvalidProgramException,“公共语言运行时检测到无效程序”。指向get方法。我的get方法模型是:
/* public class TestWrapper : AttributeWrapper //This is the source of the following MSIL
{
public string Name
{
get { return GetAttribute("Name"); }
set { SetAttribute("Name", value); }
}
}
*/
{
// Code size 17 (0x11)
.maxstack 2
.locals init ([0] string V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldstr "Name"
IL_0007: call instance string ConfigXMLParser.frmNodeBuilder/AttributeWrapper::GetAttribute(string)
IL_000c: stloc.0
IL_000d: br.s IL_000f
IL_000f: ldloc.0
IL_0010: ret
} // end of method TestWrapper::get_Name
并且生成相关属性的代码是:
public static void CreateSelfNamingProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr =
tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
typeof(string), Type.EmptyTypes);
Type[] getAttributeArgs = { typeof(string) };
Type basetype = tb.BaseType;
MethodInfo getAttrBase = basetype.GetMethod("GetAttribute", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(getAttrBase != null);
ILGenerator ilGen = getPropMthdBldr.GetILGenerator();
Label labelReturn = ilGen.DefineLabel();
ilGen.Emit(OpCodes.Nop); //The Get function starts here.
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Ldstr, "Nope.");
ilGen.EmitCall(OpCodes.Call, getAttrBase, getAttributeArgs);
ilGen.Emit(OpCodes.Stloc_0);
ilGen.Emit(OpCodes.Br_S, labelReturn);
ilGen.MarkLabel(labelReturn);
ilGen.Emit(OpCodes.Ldloc_0);
ilGen.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
MethodInfo setAttrBase
= basetype.GetMethod("SetAttribute", BindingFlags.Instance | BindingFlags.NonPublic);
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();
Type[] setParamTypes = { typeof(string) , typeof(string) };
setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Nop); //The Set method starts here
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldstr, propertyName);
setIl.Emit(OpCodes.Ldarg_1);
setIl.EmitCall(OpCodes.Call, setAttrBase, setParamTypes);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
// And this is what builds the TypeBuilder:
public static TypeBuilder GetTypeBuilder()
{
string typeSignature = "MyDynamicType";
AssemblyName an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
return tb;
}
最后,将它们放在一起:
TypeBuilder tb = GetTypeBuilder("TestType");
CreateSelfNamingProperty(tb, "Name", typeof(string));
dynamic instance = Activator.CreateInstance(tb.CreateType());
instance.Name = "Test"; //Debug shows Name is Test, but
MessageBox.Show(instance.Name);//Exception occurs here
基类非常简单:
public class AttributeWrapper
{
protected Dictionary<string, string> _attributes =
new Dictionary<string, string>();
protected void SetAttribute(string attribute, string value)
{
if (_attributes.ContainsKey(attribute))
{
_attributes[attribute] = value;
}
else
{
_attributes.Add(attribute, value);
}
}
protected string GetAttribute(string attribute)
{
return _attributes.ContainsKey(attribute) ? _attributes[attribute] : "";
}
}
您的直接问题是,您正在使用stloc.0
和ldloc.0
操作码在未定义任何本地变量的情况下读写本地变量!您可以通过在方法顶部调用以下内容来解决此问题:
ilGen.DeclareLocal(typeof(string));
现在这是东西,实际上并不需要。您反汇编并用作模板的代码显然是在Debug
模式下编译的。我可以说是由于本地原因,但主要来自nop
。这两件事在调试版本中存在,有助于调试过程,使您可以介入并查看中间值。您的吸气剂可以减少到以下IL:
ldarg.0
ldstr "Nope."
call instance string [AttributeWrapper]::GetAttribute(string)
ret
类似地,您的二传手也可以删除其nop
。
[其他注意事项:
您使用了错误的方法来发出call
指令。 EmitCall
方法is for calling varargs
methods only并接受包含varargs
参数类型的参数。那不是你这里的。这要么涉及使用varargs
约定和/或VarCall
/ TypedReference
和__makeref
/ __arglist
针对API的一些p /调用。后者是“隐藏的” C#关键字,您几乎在代码中找不到它们。追溯到.NET 2.0之前的日子,当目标ArgIterator
不是MethodInfo
时,该方法引发了异常,但情况不再如此。
您应该改用常规的varargs
方法并传递适当的Emit
或Call
* CallVirt
。
最后,我强烈建议为此目的使用OpCode
,特别是将其设置为SharpLab构建并查看IL选项卡。与编译代码然后手动拆卸相比,它[容易。
Release
。