Самый простой способ получить все MemberInfos, которые отражают состояние объекта?

Мне нужно получить все члены, которые представляют точное состояние объекта с помощью отражения. Таким образом, эти члены включают поля (FieldInfo) и авто-свойства (PropertyInfo). Я могу получить все FieldInfoс помощью

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

И получите автоматически реализованные свойства, как указано в этой ссылке:

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();
}

Как говорит ответчик, это действительно хрупко. Есть ли более стандартный способ достижения этого? Я думаю, может быть что-то с BindingFlags или так?

type.GetMembers(BindingFlags....) ?

2 ответа

Решение

Если под "точным состоянием объекта" вы просто подразумеваете все поля экземпляра, которые он содержит (если нет, можете ли вы уточнить?), То вы должны быть в состоянии сделать это с помощью одного оператора - нет существенной разницы между полями, которые вы объявляете явно, и вспомогательные поля для авто-свойств, которые компилятор добавляет для вас. Если вы определите класс:

public class FieldInfoTest
{
  private string testField;

  public string TestProperty { get; set; }
}

... вы можете получить к ним доступ одним махом с

FieldInfo[] fields = typeof(FieldInfoTest).GetFields(BindingFlags.Instance |
                                                     BindingFlags.Public | 
                                                     BindingFlags.NonPublic);

foreach (var f in fields)
{
  Console.WriteLine(f.Name);
}

Это даст:

testField
<TestProperty>k__BackingField

Ответ Джереми - лучшее, что я мог получить. Вот еще один вариант (который я изначально упоминал в самом вопросе):

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 + ">"));
}

Больше объяснения о том, как это работает здесь: /questions/11842174/kak-uznat-yavlyaetsya-li-svojstvo-avtomaticheski-realizovannyim-svojstvom-s-otrazheniem/11842183#11842183

Немного хакерский, но это дает поля и авто-свойства (в отличие от ответа Джереми, который дает только информацию о полях).

Другие вопросы по тегам