How to set a Description Attribute using PropertyBuilder class?

  • Thread starter Thread starter Chuck Hartman
  • Start date Start date
C

Chuck Hartman

I am trying to create a dynamic assembly that contains a dynamic type with
the seveal dynamically created members like the static ones shown
immediately below.

A private field like:
private string customerName;

A public property with a Description Attribute like:

[Description("This is the long description of this property.")]
public string CustomerName
{
get { return customerName; }
set { customerName = value; }
}

I have taken bits and pieces from several of the builder class examples from
MSDN and constructed the example below. It seems to work correctly except
for the Description Attribute does not seem to be present. I did not see a
direct way to set that with the Property Builder methods, so I tried the
System.Reflection.Emit.PropertyBuilder.SetCustomAttribute(). I think I must
not be understanding some part part of the picture. I would appreciate any
help. Thanks.

--
Chuck Hartman

The complete modified sample console app code follows:
using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
using System.ComponentModel;

class PropertyBuilderDemo
{
public static Type BuildDynamicTypeWithProperties()
{
AppDomain myDomain = Thread.GetDomain();
AssemblyName myAsmName = new AssemblyName();
myAsmName.Name = "MyDynamicAssembly";

AssemblyBuilder myAsmBuilder = myDomain.DefineDynamicAssembly(myAsmName,
AssemblyBuilderAccess.Run);

ModuleBuilder myModBuilder = myAsmBuilder.DefineDynamicModule("MyModule");

TypeBuilder myTypeBuilder = myModBuilder.DefineType("CustomerData",
TypeAttributes.Public);

FieldBuilder customerNameBldr = myTypeBuilder.DefineField("customerName",
typeof(string),
FieldAttributes.Private);

PropertyBuilder custNamePropBldr =
myTypeBuilder.DefineProperty("CustomerName",
PropertyAttributes.HasDefault,
typeof(string),
new Type[] { typeof(string) });

//*** begin added code
Type[] ctorParams = new Type[] { typeof(string) };
ConstructorInfo classCtorInfo =
typeof(DescriptionAttribute).GetConstructor(ctorParams);

CustomAttributeBuilder myCABuilder = new CustomAttributeBuilder(
classCtorInfo,
new object[] { "This is the long description of this property." });

custNamePropBldr.SetCustomAttribute(myCABuilder);
//*** end added code

// First, we'll define the behavior of the "get" property for CustomerName
as a method.
MethodBuilder custNameGetPropMthdBldr =
myTypeBuilder.DefineMethod("GetCustomerName",
MethodAttributes.Public,
typeof(string),
new Type[] { });

ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();

custNameGetIL.Emit(OpCodes.Ldarg_0);
custNameGetIL.Emit(OpCodes.Ldfld, customerNameBldr);
custNameGetIL.Emit(OpCodes.Ret);

// Now, we'll define the behavior of the "set" property for CustomerName.
MethodBuilder custNameSetPropMthdBldr =
myTypeBuilder.DefineMethod("SetCustomerName",
MethodAttributes.Public,
null,
new Type[] { typeof(string) });

ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();

custNameSetIL.Emit(OpCodes.Ldarg_0);
custNameSetIL.Emit(OpCodes.Ldarg_1);
custNameSetIL.Emit(OpCodes.Stfld, customerNameBldr);
custNameSetIL.Emit(OpCodes.Ret);

// Last, we must map the two methods created above to our PropertyBuilder
to
// their corresponding behaviors, "get" and "set" respectively.
custNamePropBldr.SetGetMethod(custNameGetPropMthdBldr);
custNamePropBldr.SetSetMethod(custNameSetPropMthdBldr);

return myTypeBuilder.CreateType();
}

public static void Main()
{
Type custDataType = BuildDynamicTypeWithProperties();

PropertyInfo[] custDataPropInfo = custDataType.GetProperties();
foreach (PropertyInfo pInfo in custDataPropInfo)
{
Console.WriteLine("Property '{0}' created!", pInfo.ToString());
PropertyDescriptor propDesc =
TypeDescriptor.GetProperties(custDataType)[pInfo.Name];
Console.WriteLine(" Description: '{0}' created!", propDesc.Description);
}

Console.WriteLine("---");
// Note that when invoking a property, you need to use the proper
BindingFlags -
// BindingFlags.SetProperty when you invoke the "set" behavior, and
// BindingFlags.GetProperty when you invoke the "get" behavior. Also note
that
// we invoke them based on the name we gave the property, as expected, and
not
// the name of the methods we bound to the specific property behaviors.

object custData = Activator.CreateInstance(custDataType);

custDataType.InvokeMember("CustomerName", BindingFlags.SetProperty,
null, custData, new object[]{ "Joe User" });

Console.WriteLine("The customerName field of instance custData has been
set to '{0}'.",
custDataType.InvokeMember("CustomerName", BindingFlags.GetProperty,
null, custData, new object[]{ }));

Console.WriteLine("Done");
}

}
 
Chuck Hartman said:
I am trying to create a dynamic assembly that contains a dynamic type with
the seveal dynamically created members like the static ones shown
immediately below.

A private field like:
private string customerName;

A public property with a Description Attribute like:

[Description("This is the long description of this property.")]
public string CustomerName
{
get { return customerName; }
set { customerName = value; }
}

I have taken bits and pieces from several of the builder class examples from
MSDN and constructed the example below. It seems to work correctly except
for the Description Attribute does not seem to be present. I did not see a
direct way to set that with the Property Builder methods, so I tried the
System.Reflection.Emit.PropertyBuilder.SetCustomAttribute(). I think I must
not be understanding some part part of the picture. I would appreciate any
help. Thanks.

The description attribute is *definitely* there. If you change your
display code (in the first bit) to include:

foreach (object o in pInfo.GetCustomAttributes(true))
{
Console.WriteLine ("{0}: {1}", o.GetType(), o);
if (o is DescriptionAttribute)
{
Console.WriteLine ("Description = {0}",
((DescriptionAttribute)o).Description);
}
}

then you'll see the attribute and its correct value.

So, the next question is why your other way of getting the attribute
isn't working. It *does* work with a "normal" type which isn't
dynamically created - but it *doesn't* work with a type which has been
previously dynamically created and then saved. I'll compare the IL
between those last two cases and let you know what I come up with...
 
Got it. The problem is this statement:

PropertyBuilder custNamePropBldr =
myTypeBuilder.DefineProperty("CustomerName",
PropertyAttributes.HasDefault,
typeof(string),
new Type[] { typeof(string) });

The last line is where you define the types of the parameters of the
property - but the property doesn't actually *have* any parameters.
You've effectively declared an indexer, but then given property methods
as implementation. I'm surprised it doesn't complain, to be honest.

Anyway, changing the last bit to just new Type[0] or new Type[] {},
everything works.
 
Jon, you got it; it now works as I would like. Thank you very much!

I too am a bit surprised that it doesn't complain. I modeled my example
after the example in the MSDN doc for the TypeBuilder.DefineProperty method.
Even though it doesn't complain, for correctness sake, my guess is that they
should also remove the typeof(string) from the 4th parameter on their call
to DefineProperty().
Thanks again.

Chuck Hartman
 
Chuck Hartman said:
Jon, you got it; it now works as I would like. Thank you very much!

I too am a bit surprised that it doesn't complain. I modeled my example
after the example in the MSDN doc for the TypeBuilder.DefineProperty method.
Even though it doesn't complain, for correctness sake, my guess is that they
should also remove the typeof(string) from the 4th parameter on their call
to DefineProperty().

Yes indeed. I've mailed them to suggest that.
 
Back
Top