C# Reflection : Finding Custom Attributes on a Class Property

I would like to define a custom attribute for specific properties of a class. Based on reflection it should be possible to check if the property is annotated with this attribute or not. The first Assert.AreEqual(2, infos.Length) passes but the second Assert.AreEqual(1, properties.Count) fails with properties.Count = 0. What am I missing?

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Reflection;

namespace Test.UnitTesting.Rest.ParameteterModels
{
    public class IncludableAttribute : Attribute
    {
    }

    public class TestClass
    {
        [Includable]
        public List<string> List1 { get; set; }

        public List<string> List2 { get; set; }

        public TestClass()
        {

        }
    }


    [TestClass]
    public class IncludeParamaterModelTest
    {
        [TestMethod]
        public void TestMethod1()
        {
            List<string> properties = new List<string>();

            FieldInfo[] infos = typeof(TestClass).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            Assert.AreEqual(2, infos.Length);

            foreach (FieldInfo fi in infos)
            {
                if (fi.IsDefined(typeof(IncludableAttribute), true)) properties.Add(fi.Name);
            }
            Assert.AreEqual(1, properties.Count);
        }
    }
}

This question is similar to C# Reflection : Finding Attributes on a Member Field

Jon Skeet
people
quotationmark

Your attribute is being applied to the properties. While you do have fields, they're created by the compiler - and your custom attribute isn't being applied to them. Your code is broadly equivalent to:

[CompilerGenerated]
private List<string> list1;

[Includable]
public List<string> List1 { get { return list1; } set { list1 = value; } }

[CompilerGenerated]
private List<string> list2;
public List<string> List2 { get { return list2; } set { list2 = value; } }

As you can see, there are still two fields (list1 and list2) - which is why your first assertion succeeds. However, neither of those have the Includable attribute. It's the List1 property which does.

So you need to be asking for the properties rather than the fields. I'd also do this with LINQ rather than looping explicitly:

var properties = typeof(TestClass)
                     .GetProperties(BindingFlags.NonPublic | 
                                    BindingFlags.Public | 
                                    BindingFlags.Instance)
                     .Where(p => p.IsDefined(typeof(IncludableAttribute), true)
                     .ToList();

(You may want to consider whether you really want to pick up non-public properties.)

people

See more on this question at Stackoverflow