質問

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....) ?
役に立ちましたか?

解決

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

他のヒント

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).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top