6

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?

1
  • ooh, that's fun; that'll take a few minutes for me to set up a test rig for... I'm hoping someone will spot something obvious while I do that! Commented Nov 20, 2013 at 15:58

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

6 Comments

That worked as you say. I'm feeling a little stupid for not trying to run it in a separate problem before, but I'd still be here, but with a different question. But, when I copy that working code into my "real" application it will not work. result is a list of zero length then. Same code copied into a WPF application. To exclude WPF being the culprit I also created and empty WPF application, that worked. So it is something in my "real application". Problem is figuring out what...
Such a silly mistake. Apparantly forgetting to declare TypeAttribute as public will cause this. Hadn't spotted it without your working code. Thanks.
When I try to do result[0].GetCustomAttributes() I get a FileNotFoundException on the autogenerated assembly. Got any idea what I need to do? (ofc i check that there is at least one result in the list)
@fredrik not without having a reproducible example, no; my first hunch would be RunAndSave, but other than that: I'd need to see it happening
I just managed to solve it. I had to explicitly call assemblyBuilder.Save(), then it all worked. I can live with that. Thanks for your help.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.