1

I've been curious whether it's possible to get a list of all the classes with my own attribute without explicitly iterating over all types defined in an assembly. The thing I've tried is to make attribute's constructor write all the types into a static field. For unknown reason the list of the types doesn't contain a single entry. The following code outputs 0 to the console window. Why?

[AttributeUsage(AttributeTargets.Class)]
class SmartAttribute : Attribute {
    public static List<Type> Types = new List<Type>();
    public SmartAttribute(Type type) {
        Types.Add(type);
    }
}

[SmartAttribute(typeof(Test))]
class Test {
}

class Program {
    static void Main(string[] args) {
        Console.WriteLine(SmartAttribute.Types.Count());
        foreach (var type in SmartAttribute.Types) {
            Console.WriteLine(type.Name);
        }
    }
}
polkovnikov.ph
  • 6,256
  • 6
  • 44
  • 79
  • No, you have to iterate. Attributes are metadata then they'll materialize only when you'll ask for them (then when you have an instance). Until you have a SmartAttribute instance (with GetCustomAttribute()) it won't be created (that's the reason, for example, you can't use any type as parameter). – Adriano Repetti Aug 26 '14 at 11:26
  • @AdrianoRepetti I've tried making an instance of `Test`, and it didn't helped. Do you mean they will materialize only when I'm calling `GetCustomAttributes`? That's kind of confusing behaviour for a strict (non-lazy) language. – polkovnikov.ph Aug 26 '14 at 11:28
  • Yes. If you write `new SmartAttribute(typeof(Test))` then of course constructor will be called but with `[SmartAttribute(typeof(Test))]` object doesn't exist until you explicitly ask for its instance. It's both because of performance (objects aren't built until required, count attributes on a system assemblies and you'll imagine impact) and because metadata can be inspected also by unmanaged code. – Adriano Repetti Aug 26 '14 at 11:31
  • @AdrianoRepetti Could you find a citation from C# Language Specification as an answer, so that I can close this question? I've been trying to find it myself and got lost. – polkovnikov.ph Aug 26 '14 at 12:35
  • Away from computer now but a quick search pointed me to section 17.3 of C# specifications. It _may_ be there. – Adriano Repetti Aug 26 '14 at 12:45

1 Answers1

1

What you are trying to do is not possible. Attributes are meta-data, they are not instantiated at run-time, so your constructor isn't called.

If you step back and think about why, it makes sense. In order for the code you propose to work, the first thing your the run time would have to do before executing any code, is create instances of all attributes. Then you'd run into some fun scenarios like:

 [AttributeB]
 public class AttributeA : Attribute{}

 [AttributeA]
 public class AttributeB : Attribute {}

Which constructor would go first, when would it know to stop, etc? It's reasons like this why the .Net architects decided not to execute Attributes constructors.

Now, to find all the types that are decorated with your attribute, you will need to iterate through all of the types in your assembly from within your Main method (or any other executing method):

 Assembly.GetExecutingAssembly()
     .GetTypes()
     .Where(t => Attribute.IsDefined(t, typeof(SmartAttribute)))

See get all types in assembly with custom attribute for more discussion on retrieving types decorated with attributes.

Community
  • 1
  • 1
Philip Pittle
  • 11,821
  • 8
  • 59
  • 123
  • That's exactly what I've done, though it was uneasy for me to iterate over countless types. – polkovnikov.ph Aug 26 '14 at 14:23
  • Yes, it seems a bit counter intuitive. If this is performance critical for some reason, you could always write a post compilation step to do the analysis and update a config file, that way the code is only run once and not every time the assembly is executed. – Philip Pittle Aug 26 '14 at 14:26