Why isn't TypeDescriptor.GetAttributes returning all attributes?

269 Views Asked by At

Code will show it all. You can drop this in RoslynPad or VS.

using System.ComponentModel;

[AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=true)]
class CategoryOrderAttribute : Attribute{

    public CategoryOrderAttribute(string category, int order){
        Category = category;
        Order    = order;
    }

    public string Category { get; }
    public int    Order    { get; }

    public override string ToString()
        => $"  Category:{Category}, Order:{Order}";
}

[CategoryOrder("Test", 1)]
[CategoryOrder("OtherTest", 2)]
public class TestClass{

    static TestClass(){

        var sb = new StringBuilder();

        var foundAttributesA = typeof(TestClass)
            .GetCustomAttributes(typeof(CategoryOrderAttribute), true)
            .OfType<CategoryOrderAttribute>();

        sb.AppendLine("Test A");
        foreach(var foundAttribute in foundAttributesA)
            sb.AppendLine(foundAttribute.ToString());

        var foundAttributesB = TypeDescriptor.GetAttributes(typeof(TestClass))
            .OfType<CategoryOrderAttribute>();

        sb.AppendLine("Test B");
        foreach(var foundAttribute in foundAttributesB)
            sb.AppendLine(foundAttribute.ToString());

        // Add new attribute programmatically
        TypeDescriptor.AddAttributes(typeof(TestClass),
            new CategoryOrderAttribute("AddedInCode", 42));

        var foundAttributesC = typeof(TestClass)
            .GetCustomAttributes(typeof(CategoryOrderAttribute), true)
            .OfType<CategoryOrderAttribute>();

        sb.AppendLine("Test C");
        foreach(var foundAttribute in foundAttributesC)
            sb.AppendLine(foundAttribute.ToString());

        var foundAttributesD = TypeDescriptor.GetAttributes(typeof(TestClass))
            .OfType<CategoryOrderAttribute>();

        sb.AppendLine("Test D");
        foreach(var foundAttribute in foundAttributesD)
            sb.AppendLine(foundAttribute.ToString());

        Console.WriteLine(sb.ToString());
    }
}

_ = new TestClass();
Console.WriteLine("Tests Done!");

The output is as follows:

Test A
  Category:Test, Order:1
  Category:OtherTest, Order:2
Test B
  Category:Test, Order:1
Test C
  Category:Test, Order:1
  Category:OtherTest, Order:2
Test D
  Category:AddedInCode, Order:42

So my questions are:

  1. Why does it only get the first one in 'Test B'
  2. Why does it only get the programmatically added one in 'Test D'
  3. Why does 'Test C' not get the programmatically added one?
0

There are 0 best solutions below