To expand on the title, I'm using Reflection to generate a custom subclass that will eventually have an arbitrary number of string properties which they'll internally store in a dictionary. In this case I'm just using one. I've used Ildasm.exe to get the MSIL that I need for the My Set method works, as the debugger shows the value I'm assigning, but when I try to read it back I get an InvalidProgramException, "Common Language Runtime detected an invalid program." which points back to the get method. My get method model is:
/* 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
And code generating the property in question is:
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;
}
Finally, putting it all together:
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
And the base class is pretty straightforward:
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] : "";
}
}
Aucun commentaire:
Enregistrer un commentaire