lundi 20 juillet 2015

C#: Get with Reflection fields that are not generated by the compiler

Recently I was writing a method to construct a graph with the dependencies between classes using Reflection and found the following problem. My method analyzes the return type of property, generic arguments of class definition and instance fields of those classes.

For inspection of instance fields of class I use the following method.

public static IEnumerable<FieldInfo> GetFields(Type classType)
{
    return classType
        .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
        .Select(f => f.FieldType);
}  

To test it, I write the following class definition:

static void Main(string[] args)
{
    foreach (var fieldInfo in GetFields(typeof(A)))
        Console.WriteLine(fieldInfo.Name);

    Console.ReadKey();
}

class A
{
    private ulong? _field1;

    public byte PropertyA { get; set; }

    public int PropertyB { get; set; }

    public bool PropertyC { get; set; }
}

I was in shock for a few seconds, to see the result. It was when I remembered that .NET generates an instance field, Set and Get methods to emulate the properties.

enter image description here

When I inspected with .NET Reflector the library to see the code generated by the compiler, I find the following definition.

class A
{
    private ulong? _field1; 

    [CompilerGenerated]
    private Byte <PropertyA>k__BackingField;

    [CompilerGenerated]
    private Int32 <PropertyB>k__BackingField;

    [CompilerGenerated]
    private bool <PropertyC>k__BackingField;
}

So I modified the method to exclude the fields with CompilerGenerated attribute and his name match with some property.

public static IEnumerable<FieldInfo> GetFields(Type classType)
{
    var regex = new Regex(@"^<(?<PropertyName>\w+)>\w+$");

    var fieldInfoes = classType
            .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

    foreach (var fieldInfo in fieldInfoes)
    {
        if (fieldInfo.GetCustomAttribute<CompilerGeneratedAttribute>() == null)
            yield return fieldInfo;
        else
        {
            var match = regex.Match(fieldInfo.Name);
            if (!match.Success) 
                continue;

            var propertyName = match.Groups[@"PropertyName"].Value;
            if (classType.GetProperty(propertyName) == null)
                yield return fieldInfo;
        }
    }
}

QUESTIONS

  • There is some combination of BindingFlags I'm missing for these fields?
  • There is another way of getting these fields because this code appears to me that is like killing a mosquito with a bazooka?

You can download the complete code here.





Aucun commentaire:

Enregistrer un commentaire