Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

Просмотр статических полей класса с помощью рефлексии типов.

#c_sharp #net #design #рефлексия


класс Building используется как более гибкая альтернатива Enum'ам (Type safe enum
pattern ).
Хотелось бы иметь возможность просматривать доступные статические поля в рантайме.
В моем решении каждый такой класс должен будет однообразно реализовать статический
метод GetAllFields() , что мне не особо нравится.
Есть ли более элегантное решение данной проблемы , чем мое?
public abstract class ABase {  
  protected static IEnumerable GetAllField(Type type) {
    var res = from x in type.GetFields()
              where x.IsStatic == true
              where x.IsPublic == true
              select x.GetValue(null);
    ABase obj;
    foreach(var item in res) {
        if(!(item is ABase))
            continue;
        obj = item as ABase;
        yield return obj;
    }
  }
}

public sealed class Building : ABase {
  public static readonly Building House = new Building();
  public static readonly Building Castle = new Building();

  public static System.Collections.Generic.IEnumerable GetAllFields() {
    return GetAllField(typeof(Building));
  }

}
    


Ответы

Ответ 1



Попробуйте так: public abstract class ABase { public IEnumerable GetAllFields() { return GetType().GetFields(BindingFlags.Static) .Select(fi => fi.GetValue(null)) .Where(f => f is ABase) .Cast(); } } Для статического метода трюк не сработает :(

Ответ 2



Я бы решил эту задачу таким образом: public abstract class ABase { protected static IEnumerable GetAllField() => typeof(T) .GetFields(BindingFlags.Static | BindingFlags.Public) .Select(x => x.GetValue(null)) .OfType(); public string Name { get; protected set; } } public sealed class Building : ABase { public static readonly Building House = new Building(nameof(House)); public static readonly Building Castle = new Building(nameof(Castle)); private Building(string name) { Name = name; } public static IEnumerable GetAllFields() => GetAllField(); } Вы можете обойтись без свойства Name в ABase, тогда метод GetAllField может возвращать IEnumerable для Вашего примера в комментариях. А в моем примеры Вы можете проходить коллекию так: foreach (var item in Building.GetAllFields()) { Console.WriteLine(item.Name); // Ваша логика. } UPD: Можно автоматизировать создание read only полей, например так: public abstract class ABase { protected static IEnumerable GetAllField() => typeof(T) .GetFields(BindingFlags.Static | BindingFlags.Public) .Select(x => x.GetValue(null)) .OfType(); protected static void Init() => typeof(T) .GetFields(BindingFlags.Static | BindingFlags.Public) .ToList() .ForEach(x => x.SetValue(null, Activator.CreateInstance(typeof(T), x.Name))); public string Name { get; protected set; } } public sealed class Building : ABase { public static readonly Building House; public static readonly Building Castle; public Building(string name) { Name = name; } static Building() { Init(); } public static IEnumerable GetAllFields() => GetAllField(); } Но не могу не согласить с VladD что лучше перейти к instance-методам.

Комментариев нет:

Отправить комментарий