简体   繁体   中英

C#: Get field/property/method value (which is either string or uint) by name using reflection

I'm trying to parse some data out of many old assemblies from a 5-year old open source project and have mostly got it working, but the code is extremely verbose.

Edit: Also, I'm, trapped on .Net Framework 3.5

There are two members I'm interested in are named "Version" and "TargetVersion" .

Until recently the "Version" member was a string defined in various ways. It's now replaced with a "ModVersion" member that grabs from assembly version. Some examples:

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;

The TargetVersion member only has two forms: public static readonly uint or public const uint .

So currently I get the member type, and then have nested if .. else if ... for various member types like so:

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

And then I've got a similar pile of code for getting the "TargetVersion" which is a uint . And now I'll need to add something else to get the new "ModVersion" in cases where "Version" is not found...

Is there any way I can reduce duplication? For example, is it possible to use generics? (I'm still newbie at C#) to avoid duplicating the code for the string vs uint vs. Version ? And is there a way to chop down the amount of code that deals with the different member types?

I've seen something like this elsewhere on SO but no idea how to adapt it to my use case:

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

What I'm ultimately hoping to achieve is something where I can specify a type and member name and just get the value back. Sort of a bool TryGetMemberValue<T>(Type thing, string memberName, out <T>value) method.

Hope this question isn't too dumb, I'm still learning the basics :o

You were nearly there, maybe something like this

Note : I have just returned a string. You could pass another generic parameter in if you like and cast the results, however i assume all the types you want to work with will override 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;

}

or if you really want the type explicitly

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

Full Demo Here

Update

Here is a version where you give the type, and it spins up an instance to get the instance members, then disposes if it needs to

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

Usage

Assembly asm = <some assembly>;

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM