views:

256

answers:

2

Consider the following code:

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Enum, AllowMultiple = true)]
    public class TransitionToAttribute : Attribute
    {
      public readonly object Next;
      public TransitionToAttribute(object next)
      {
        Next = next;
      }
    }

    [TransitionToAttribute(DirectedGraph.A)]
    public enum DirectedGraph
    {
      [TransitionToAttribute(DirectedGraph.B)]
      A,

      [TransitionToAttribute(null)]
      B
    }

The code compiles fine. Now I want to define a similar enum in a dynamic assembly with code like:

  AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
    new AssemblyName("TestAssembly"), AssemblyBuilderAccess.RunAndSave);
  ModuleBuilder mb = ab.DefineDynamicModule("TestModule");
  EnumBuilder eb = mb.DefineEnum("DirectedGraph2", TypeAttributes.Public, typeof(int));
  FieldBuilder fb = eb.DefineLiteral("A", 0);
  FieldBuilder fb2 = eb.DefineLiteral("B", 1);
  eb.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(TransitionToAttribute).GetConstructors().First(), new object[] { ??? }));
  Type created = eb.CreateType();

What is the "???" that I pass to the attribute constructor? "???" needs to be some representation of the "A" literal in the enum I am in the process of defining. I've tried passing fb, fb.GetValue(null), and calling various combinations of Enum.Parse(), Enum.ToObject(), Enum.GetValues(), and other methods but nothing seems to work.

The obvious replacement for ??? is the underlying integer enum value (e.g., 0 for A, 1 for B, etc.), but this doesn't work the way I need it to. At some point, I want to do something like

TransitionToAttribute attr = GetCustomAttribute(...)
Type enumType = attr.Next.GetType();

and determine the enum type that way. This is possible in the first, normally compiled example. But if I pass the underlying enum value to the dynamically created attribute, the type information is lost and enumType is reported as Int32.

+1  A: 

Try calling CreateType before calling SetCustomAttribute (see the sample code for SetCustomAttribute).

Type created = eb.CreateType();
eb.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(TransitionToAttribute).GetConstructors().First(),
    new object[] { Enum.Parse(created, "A") }));
ESRogs
A: 

You're right, ESRogs, that works for setting an attribute on the enum. But I also need to set an attribute on the enum literal (fb).

  Type created = eb.CreateType();
  eb.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(TransitionToAttribute).GetConstructors().First(),
    new object[] { Enum.Parse(created, "A") }));
  fb.SetCustomAttribute(new CustomAttributeBuilder(
    typeof(TransitionToAttribute).GetConstructors().First(),
    new object[] { Enum.Parse(created, "A") }));

The first call to SetCustomAttribute succeeds. The second fails with an InvalidOperationException: Unable to change after type has been created.

Peter Winton