Pergunta

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....) ?
Foi útil?

Solução

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

Outras dicas

Jeremy's answer is the best I could get. Here's another alternative (which I originally mentioned in question itself):

public static IEnumerable<MemberInfo> GetStateMembers(this Type t)
{
    return t.GetMembers(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Where(m => m.MemberType == MemberTypes.Field && !((FieldInfo)m).Name.Contains('<')
                     || m.MemberType == MemberTypes.Property && ((PropertyInfo)m).IsAutoProperty());
}

public static bool IsAutoProperty(this PropertyInfo prop)
{
    if (!prop.CanWrite || !prop.CanRead)
        return false;

    return prop.DeclaringType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                             .Any(f => f.Name.Contains("<" + prop.Name + ">"));
}

More explanation on how that works here: https://stackoverflow.com/a/16506710/661933

Little bit hackish, but this gives fields and auto-properties (unlike Jeremy's answer which gives only field infos).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top