1

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....) ?
Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • is this for specific objects types, or for any object type? if you want to get state for specific types, and you own their code, you can add your own special attribute [ObjectState] to mark those fields – omer schleifer May 12 '13 at 06:05
  • @omerschleifer for any object type. I'm thinking of handling it generically, than go and modify all those classes. – nawfal May 12 '13 at 06:06
  • Do you need to treat auto-property backing fields differently from regular fields? In my experience, `GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);` will grab them. Or am I misunderstanding the question? – Jeremy Todd May 12 '13 at 06:42
  • @JeremyTodd No I need not treat them differently, I just need them. Let me try your example. If it works, I'll inform you, and you may kindly make that an answer :) – nawfal May 12 '13 at 06:45
  • @nawfal not sure what you want with 'public facing' but it sounds like you're trying to write your own serialization process to output the file to a text (or similar) file that be imported/exported from other applications. In which case, do a search for `Serialization` the work is already done for binary, xml and json based formats. – Seph May 12 '13 at 09:27

2 Answers2

2

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 Todd
  • 3,261
  • 1
  • 18
  • 17
  • Jeremy, +1 for the idea, might work in my case. But I would love to get the property instead (as `PropertyInfo`) – nawfal May 12 '13 at 07:02
  • @nawfal Any particular reason you have a special interest in the auto-properties, though? I mean, apart from the fact that they're implemented by compiler sugar, they're pretty much the same as regular properties under the hood. – Jeremy Todd May 12 '13 at 07:12
  • In my case I need a handle to autoproperties for the fact that they are standalone member (from outside world at least), but not normal properties (which will have backing field). For eg, if there is a `Person` class like this: `int age; int Age { get { return age; } } string Name { get; set; }`, what all determine the a particular state of the object? It has to be the fields (since they form the basis of properties; externally the expose something else), and auto-properties which doesn't have backing fields - hence, `age` and `Name` for me. – nawfal May 12 '13 at 07:18
  • Auto-properties do have backing fields, though. – Jeremy Todd May 12 '13 at 07:24
  • Jeremy, they do, that's why I used "outside world" first. I hope you understood why I need auto-properties specifically. Since, as you said, they are properties in every sense under the hood, it will be nice if I get them as properties itself. – nawfal May 12 '13 at 07:27
  • I'm afraid I don't quite understand yet. :) I'm not getting why you want `PropertyInfo` for `string Name { get; set; }` but not for `int age; int Age { get { return age; } set { age = value; } }`, since they're more or less exactly the same under the hood. – Jeremy Todd May 12 '13 at 07:29
  • that was a very stripped down example I gave you. The key is this point which I said "externally the expose something else". For example, take this `Person` class: `int dob; int Age { get { return CalculateAge(dob); } } string Name { get; set; }`. Here internally the state of the object is represented by backing fields, not what they expose to outside world isn't it? Take a more complex scenario like this: `int value; int Value { get { return Math.Abs(value); } } string Name { get; set; }`. – nawfal May 12 '13 at 07:36
  • In scenarios like this there is no way the actual state can be obtained from properties (but auto-properties help). – nawfal May 12 '13 at 07:37
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29790/discussion-between-jeremy-todd-and-nawfal) – Jeremy Todd May 12 '13 at 08:32
0

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

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368