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
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.)
See more on this question at Stackoverflow