Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Attribute from Dynamically generated Class

Tags:

c#

c#-4.0

I have run in to a little bit of a problem which is not solved by the generally available solutions to seemingly the same problem.

Consider:

I have a set of dynamically generated classes, inheriting from a known base Class (lets call it BaseClass). These dynamically generated classes also have dynamically generated Properties with associated attributes.

The attributes are also of a custom class, though not dynamically generated:

[AttributeUsage(AttributeTargets.Property)]
class TypeAttribute: Attribute
{
    private Type _type;
    public Type Type
    {
        get { return _type; }
    }

    public TypeAttribute(Type t)
    {
        _type = t;
    }
}

Then I want to, runtime of course, fetch the value of this assigned attribute:

List<PropertyInfo> result = target.GetType()
  .GetProperties()
  .Where(
     p =>
        p.GetCustomAttributes(typeof(TypeAttribute), true)
        //.Where(ca => ((TypeAttribute)ca).)
        .Any()
     )
  .ToList();

where target is a subclass of BaseClass. The List result is however empty, and this baffles me.

I add the attribute using

PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, 
           PropertyAttributes.HasDefault, propertyType, null);
ConstructorInfo classCtorInfo = typeof(TypeAttribute).
           GetConstructor(new Type[] { typeof(Type) });
CustomAttributeBuilder myCABuilder = new CustomAttributeBuilder(
           classCtorInfo, new object[] { getType(dataType) });
propertyBuilder.SetCustomAttribute(myCABuilder);

where dataType is the type to store in the attribute and tb is the TypeBuilder for the class.

If I do getCustomAttributes() on the property, I get the expected attributes except the one I'm looking for. But if I do getCustomAttributesData() I get all of them, but the one I'm looking for is of type CustomAttributeData and is not castable to TypeAttribute (if i examine the instance in the VS debugger i can see that the contained information is for a TypeAttribute). I'm guessing that this is a symptom of the problem, but I cannot find the cause - much less the solution.

Can anybody point out to me why the result list is empty?

like image 806
fredrik Avatar asked Nov 12 '22 18:11

fredrik


1 Answers

Works fine for me; are you sure your property and getter or setter method(s) exist sufficiently that it actually shows as a property?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
[AttributeUsage(AttributeTargets.Property)]
public class TypeAttribute : Attribute
{
    private Type _type;
    public Type Type
    {
        get { return _type; }
    }

    public TypeAttribute(Type t)
    {
        _type = t;
    }
}

public class BaseClass
{

}

static class Program
{
    static void Main()
    {
        var aName = new AssemblyName("MyAssembly");
        var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
                aName, AssemblyBuilderAccess.RunAndSave);
        var mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
        var tb = mb.DefineType("MyType", TypeAttributes.Public, typeof(BaseClass));

        var propertyName = "MyProperty";
        var propertyType = typeof(int);
        var propertyBuilder = tb.DefineProperty(propertyName,
           PropertyAttributes.HasDefault, propertyType, null);
        var classCtorInfo = typeof(TypeAttribute).
                   GetConstructor(new Type[] { typeof(Type) });

        Type tArg = typeof(float); // for no real reason
        var myCABuilder = new CustomAttributeBuilder(
            classCtorInfo, new object[] { tArg });
        propertyBuilder.SetCustomAttribute(myCABuilder);

        var field = tb.DefineField("myField", propertyType, FieldAttributes.Private);
        var getter = tb.DefineMethod("get_" + propertyName,
            MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Public,
            propertyType, Type.EmptyTypes);
        propertyBuilder.SetGetMethod(getter);
        var il = getter.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldfld, field);
        il.Emit(OpCodes.Ret);
        var setter = tb.DefineMethod("set_" + propertyName,
            MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Public,
            typeof(void), new[] { typeof(int) });
        il = setter.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Stfld, field);
        il.Emit(OpCodes.Ret);
        propertyBuilder.SetSetMethod(setter);
        var target = Activator.CreateInstance(tb.CreateType());        

        List<PropertyInfo> result = target.GetType()
          .GetProperties()
          .Where(
             p =>
                p.GetCustomAttributes(typeof(TypeAttribute), true)
                 //.Where(ca => ((TypeAttribute)ca).)
                .Any()
             ).ToList();
    }
}

Also, it is more efficient to use Attribute.IsDefined if you just care that it exits.

List<PropertyInfo> result = target.GetType().GetProperties()
      .Where(p => Attribute.IsDefined(p, typeof(TypeAttribute), true)).ToList();

Note also that I had to spoof the getType(dataType) stuff - I couldn't see what that was doing as it wasn't in the question. Make sure it returns a Type.

like image 149
Marc Gravell Avatar answered Nov 14 '22 23:11

Marc Gravell