Search code examples
c#reflectionautomatic-propertiesfieldinfo

Simplest way to get all MemberInfos that reflect the state of an object?


I need to get all members that represent the exact state of an object using reflection. So these members include fields (FieldInfo) and auto-properties (PropertyInfo). I can get all the FieldInfos using

type.GetFields(); //ok some flags needed here

And get auto implemented properties as mentioned in this link:

public static bool MightBeCouldBeMaybeAutoGeneratedInstanceProperty(this PropertyInfo info)
{
    bool mightBe = info.GetGetMethod().HasCompilerGeneratedAttribute();
    if (!mightBe)
        return false;

    bool maybe = info.DeclaringType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                                   .Where(f => f.Name.Contains(info.Name))
                                   .Where(f => f.Name.Contains("BackingField"))
                                   .Where(f => f.HasCompilerGeneratedAttribute())
                                   .Any();

    return maybe;
}

public static bool HasCompilerGeneratedAttribute(this MemberInfo mi)
{
    return mi.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any();
}

As the answerer says this is indeed brittle. Is there are more standard way of achieving this? I'm thinking, may be something with BindingFlags or so?

type.GetMembers(BindingFlags....) ?

Solution

  • If by "exact state of the object" you simply mean all the instance fields it contains (if not, can you clarify?), then you should be able to do it with a single statement -- there's no significant difference between fields you declare explicitly, and backing fields for auto-properties that the compiler adds for you. If you define a class:

    public class FieldInfoTest
    {
      private string testField;
    
      public string TestProperty { get; set; }
    }
    

    ...you can access them all in one fell swoop with

    FieldInfo[] fields = typeof(FieldInfoTest).GetFields(BindingFlags.Instance |
                                                         BindingFlags.Public | 
                                                         BindingFlags.NonPublic);
    
    foreach (var f in fields)
    {
      Console.WriteLine(f.Name);
    }
    

    This will yield:

    testField
    <TestProperty>k__BackingField