简体   繁体   中英

C# Reflection: Get info for all members of class and base classes

When I run the following code, it only returns a MethodInfo / FieldInfo /etc. that belongs directly to the Type that I'm searching for the info object in. How do I find the info object regardless of the fact that it resides in a base class and could be private?

obj.GetType().GetMethod(methodName, bindingFlags);

Well, you answered your own question, but as far as I understood, you main requirement is How do I find the info object regardless of where it is found in the hierarchy?

You don't need recursion here to get all members in the full hierarchy. You can use GetMembers function on a Type and it will return all members including all base classes.

Next code example demonstrates this:

var names = 
    typeof(MyClass).GetMembers()
                   .Select (x => x.Name);

Console.WriteLine (string.Join(Environment.NewLine, names));

for such structure

class MyClass : Base
{
    public string Name { get; set; }
    public string Surname { get; set; }
}

class Base
{
    public string Name { get; set; }
}

returns

get_Name
set_Name
get_Surname
set_Surname
get_Name
set_Name
ToString
Equals
GetHashCode
GetType
.ctor
Name
Surname

note that get_Name accessor for auto-property appears twice because MyClass hides Name property of the base class. Also note ToString , GetType and other methods, defined in object class

The following code will look through each base class of an object if the info object is not found in the child class. Note that though it will return the base class info object, it will return the object that it "runs into first", so if you have a variable called _blah in your child class and a variable called _blah in a base class, then the _blah from the child class will be returned.

public static MethodInfo GetMethodInfo(this Type objType, string methodName, BindingFlags flags, bool isFirstTypeChecked = true)
{
    MethodInfo methodInfo = objType.GetMethod(methodName, flags);
    if (methodInfo == null && objType.BaseType != null)
    {
        methodInfo = objType.BaseType.GetMethodInfo(methodName, flags, false);
    }
    if (methodInfo == null && isFirstTypeChecked)
    {
        throw new MissingMethodException(String.Format("Method {0}.{1} could not be found with the following BindingFlags: {2}", objType.ReflectedType.FullName, methodName, flags.ToString()));
    }
    return methodInfo;
}

public static FieldInfo GetFieldInfo(this Type objType, string fieldName, BindingFlags flags, bool isFirstTypeChecked = true)
{
    FieldInfo fieldInfo = objType.GetField(fieldName, flags);
    if (fieldInfo == null && objType.BaseType != null)
    {
        fieldInfo = objType.BaseType.GetFieldInfo(fieldName, flags, false);
    }
    if (fieldInfo == null && isFirstTypeChecked)
    {
        throw new MissingFieldException(String.Format("Field {0}.{1} could not be found with the following BindingFlags: {2}", objType.ReflectedType.FullName, fieldName, flags.ToString()));
    }
    return fieldInfo;
}

I altered bsara's implementation to be able to find private members.

I use it like this:

public static void Save(string filename, object obj)
{
    try
    {
        using Stream s = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);
        var b = new BinaryFormatter();
        b.Serialize(s, obj);
    }
    catch(SerializationException e)
    {
        var type= e.Message.Split("in Assembly")[0].Replace("Type", string.Empty).Replace("'", string.Empty).Trim();
        var assembly=e.Message.Split("in Assembly")[1].Split("'")[1];
        var atype= Type.GetType(type);

        string path = FindObject(new Stack<object>(new object[] { obj }), atype, "[myself]");

        throw new SerializationException($"Could not serialize path {path} in {obj.GetType().Name} due to not being able to process {type} from {assembly}. see inner exception for details", e);
    }            
}

the methods with a small update:

private static bool TrySerialize(object obj)
{
    if(obj == null)
        return true;
    var stream = new MemoryStream();
    var bf = new BinaryFormatter();
    try
    {
        bf.Serialize(stream, obj);
    }
    catch(SerializationException)
    {
        return false;
    }
    return true;
}
private static string FindObject(Stack<object> self, Type typeToFind, string path)
{
    var _self = self.Peek();
    if(self.Where(x => x.Equals(_self)).Count() > 1) return null;
    foreach(var prop in _self.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic).Where(x => !x.GetCustomAttributes(true).Any(y => y is XmlIgnoreAttribute)))
    {
        switch(prop.MemberType)
        {
            case System.Reflection.MemberTypes.Property:
                {
                    var line = string.Format("{0}::{1}", path, prop.Name);
                    var _prop = prop as PropertyInfo;

                    if(_prop.GetIndexParameters().Count() > 0) break;

                    if(typeToFind.IsAssignableFrom(_prop.PropertyType))
                        return line;


                    if(_prop.PropertyType.IsPrimitive || _prop.PropertyType == typeof(DateTime) || _prop.PropertyType == typeof(string))
                        continue;

                    var subInst = _prop.GetValue(_self, new object[0]);
                    if(subInst == null)
                        continue;

                    if(!TrySerialize(subInst))
                    {
                        System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize {0}\n", line));
                    }

                    self.Push(subInst);
                    var result = FindObject(self, typeToFind, line);
                    self.Pop();
                    if(result != null)
                        return result;
                }
                break;
            case System.Reflection.MemberTypes.Field:
                {
                    var line = string.Format("{0}::*{1}", path, prop.Name);
                    var _prop = prop as FieldInfo;

                    if(typeToFind.IsAssignableFrom(_prop.FieldType))
                        return line;

                    if(_prop.FieldType.IsPrimitive || _prop.FieldType == typeof(DateTime) || _prop.FieldType == typeof(string))
                        continue;

                    var subInst = _prop.GetValue(_self);
                    if(subInst == null)
                        continue;

                    if(!TrySerialize(subInst))
                    {
                        System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize field {0}\n", line));
                    }

                    self.Push(subInst);
                    var result = FindObject(self, typeToFind, line);
                    self.Pop();
                    if(result != null)
                        return result;
                }
                break;

            case System.Reflection.MemberTypes.Event:
                {
                    var line = string.Format("{0}::!{1}", path, prop.Name);
                    var _prop = prop as EventInfo;


                    if(typeToFind.IsAssignableFrom(_prop.EventHandlerType))
                        return line;

                    var field =  _self.GetType().GetField(_prop.Name,
                BindingFlags.NonPublic |BindingFlags.Instance |BindingFlags.GetField);


                    if(field != null && !field.GetCustomAttributes(true).Any(x => x is NonSerializedAttribute)  && !TrySerialize(field.GetValue(_self)))
                    {
                        System.Diagnostics.Debugger.Log(0, "", string.Format("Cannot serialize event {0}\n", line));
                    }


                }
                break;

            case System.Reflection.MemberTypes.Custom:
                {

                }
                break;
            default: break;
        }


    }
    if(_self is IEnumerable)
    {
        var list = (_self as IEnumerable).Cast<object>();
        var index = 0;
        foreach(var item in list)
        {
            index++;
            self.Push(item);
            var result = FindObject(self, typeToFind, string.Format("{0}[{1}]", path, index));
            self.Pop();
            if(result != null)
                return result;
        }
    }

    return null;
}

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