C#: Получить значение поля / свойства / метода (которое является либо строкой, либо uint) по имени, используя отражение - PullRequest
2 голосов
/ 15 февраля 2020

Я пытаюсь проанализировать некоторые данные из многих старых сборок из 5-летнего проекта с открытым исходным кодом, и в основном он работает, но код очень многословен.

Редактировать: Кроме того, я Я в ловушке. Net Framework 3.5

Есть два члена, которые меня интересуют, с именами "Version" и "TargetVersion".

До недавнего времени член "Version" не был string определяется различными способами. Теперь он заменен элементом "ModVersion", который получает версию сборки. Некоторые примеры:

public string Version => "1.4";

public static readonly string Version = "1.8.14";

public const string Version = "2.8";

public static Version ModVersion => typeof(MainClass).Assembly.GetName().Version;

Элемент TargetVersion имеет только две формы: public static readonly uint или public const uint.

Так что в настоящее время я получаю тип элемента, а затем вложил if .. else if ... для различных типов членов, таких как:

                    string dirty = string.Empty;

                    MemberTypes memberType = GetMemberType(mod, "Version");

                    if (memberType == MemberTypes.Property) {
                        Log.Info("It's a property");
                        dirty = mod
                            .GetProperty("Version", PUBLIC_STATIC)
                            .GetValue(mod, null)
                            .ToString();
                    } else if (memberType == MemberTypes.Field) {
                        Log.Info("It's a field");
                        dirty = mod
                            .GetField("Version", PUBLIC_STATIC)
                            .GetValue(mod)
                            .ToString();
                    } else if (memberType == MemberTypes.Method) {
                        Log.Info("It's a method");
                        dirty = mod
                            .GetMethod("Version", PUBLIC_STATIC)
                            .Invoke(null, null)
                            .ToString();
                    } else {
                        Log.Info("Version: Unsupported member type or not found");
                    }

А потом у меня есть похожая куча кода для получения "TargetVersion", который является uint. И теперь мне нужно будет добавить что-то еще, чтобы получить новый "ModVersion" в случаях, когда "Version" не найден ...

Можно ли как-нибудь уменьшить дублирование? Например, можно ли использовать дженерики? (Я все еще новичок ie на C#), чтобы избежать дублирования кода для string против uint против Version? И есть ли способ сократить количество кода, относящегося к различным типам элементов?

Я видел что-то подобное в SO, но не знаю, как адаптировать его к моему варианту использования:

MemberInfo info = type.GetField(memberName) as MemberInfo ??
    type.GetProperty(memberName) as MemberInfo;

То, чего я в конечном итоге надеюсь достичь, это то, где я могу указать тип и имя члена и просто получить значение обратно. Вроде метода bool TryGetMemberValue<T>(Type thing, string memberName, out <T>value).

Надеюсь, этот вопрос не слишком тупой, я все еще изучаю основы: o

1 Ответ

1 голос
/ 15 февраля 2020

Вы были почти там, возможно, что-то вроде этого

Примечание : я только что возвратил строку. Вы можете передать другой универсальный параметр c, если хотите, и привести результаты, однако я предполагаю, что все типы, с которыми вы хотите работать, переопределят ToString

public static bool TryGetValue<T>(T instance, string name, out string value)
{
   value = default;

   var member = typeof(T).GetMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                         .FirstOrDefault();

   if (member == null)return false;

   switch (member.MemberType)
   {
      case MemberTypes.Field:
         value = (member as FieldInfo)?.GetValue(instance).ToString();
         break;
      case MemberTypes.Method:
         value = (member as MethodInfo)?.Invoke(instance, null).ToString();
         break;
      case MemberTypes.Property:
         value = (member as PropertyInfo)?.GetValue(instance).ToString();
         break;
      default:
         return false;
   }

   return true;

}

или если вам действительно нужен тип явно

public static bool TryGetValue<T,T2>(T instance, string name, out string value)
{
   ...

   switch (member.MemberType)
   {
      case MemberTypes.Field:
         value = (T2)(member as FieldInfo)?.GetValue(instance);
      ...
}

Полная демонстрация здесь

Обновление

Вот версия, в которой вы указываете тип, и он раскручивает экземпляр для получения членов экземпляра, а затем удаляет, если ему нужно

public static T GetValue<T>(object instance , MemberInfo member)
{
   switch (member.MemberType)
   {
      case MemberTypes.Field: return (T)(member as FieldInfo)?.GetValue(instance);
      case MemberTypes.Method: return (T)(member as MethodInfo)?.Invoke(instance, null);
      case MemberTypes.Property: return (T)(member as PropertyInfo)?.GetValue(instance);
      default:return default;
   }
}

public static bool TryGetValue<T>(Assembly asm, string className, string name, out T value)
{
   value = default;    
   var type = asm.GetType(className);    
   var instance = Activator.CreateInstance(type);

   try
   {
      var member = type.GetMember(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                       .FirstOrDefault();

      if (instance == null || member == null) return false;

      value = GetValue<T>(instance, member);
   }
   finally
   {
      (instance as IDisposable)?.Dispose();
   }

   return true;   
}

Использование

Assembly asm = <some assembly>;

if(TryGetValue<uint>(asm, "MyNameSpace.Test1", "TargetVersion", out var out1))
     Console.WriteLine(out1);
...