Why doesn't Type.GetFields() return backing fields in a base class?

做~自己de王妃 提交于 2019-12-05 06:32:19

A field being a backing field has no influence on reflection. The only relevant property of backing fields is that they are private.

Reflection functions don't return private members of base classes, even if you use FlattenHierarchy. You will need to loop manually over your class hierarchy and ask for private fields on each one.

I think FlattenHierarchy is written with the intent to show all members visible to code in the class you look at. So base members can be hidden/shadowed by members with the same name in a more derived class and private members are not visible at all.

Here is a revised version using HashSet:

public static FieldInfo[] GetFieldInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags)
{
    FieldInfo[] fieldInfos = type.GetFields(bindingFlags);

    // If this class doesn't have a base, don't waste any time
    if (type.BaseType == typeof(object))
    {
        return fieldInfos;
    }
    else
    {   // Otherwise, collect all types up to the furthest base class
        var currentType = type;
        var fieldComparer = new FieldInfoComparer();
        var fieldInfoList = new HashSet<FieldInfo>(fieldInfos, fieldComparer);
        while (currentType != typeof(object))
        {
            fieldInfos = currentType.GetFields(bindingFlags);
            fieldInfoList.UnionWith(fieldInfos);
            currentType = currentType.BaseType;
        }
        return fieldInfoList.ToArray();
    }
}

private class FieldInfoComparer : IEqualityComparer<FieldInfo>
{
    public bool Equals(FieldInfo x, FieldInfo y)
    {
        return x.DeclaringType == y.DeclaringType && x.Name == y.Name;
    }

    public int GetHashCode(FieldInfo obj)
    {
        return obj.Name.GetHashCode() ^ obj.DeclaringType.GetHashCode();
    }
}

Thanks go to @CodeInChaos for the quick and complete answer!

In case anyone else stumbles across this, here's a quick workaround that follows the fields up to the furthest base class.

/// <summary>
///   Returns all the fields of a type, working around the fact that reflection
///   does not return private fields in any other part of the hierarchy than
///   the exact class GetFields() is called on.
/// </summary>
/// <param name="type">Type whose fields will be returned</param>
/// <param name="bindingFlags">Binding flags to use when querying the fields</param>
/// <returns>All of the type's fields, including its base types</returns>
public static FieldInfo[] GetFieldInfosIncludingBaseClasses(
    Type type, BindingFlags bindingFlags
) {
    FieldInfo[] fieldInfos = type.GetFields(bindingFlags);

    // If this class doesn't have a base, don't waste any time
    if(type.BaseType == typeof(object)) {
        return fieldInfos;
    } else { // Otherwise, collect all types up to the furthest base class
        var fieldInfoList = new List<FieldInfo>(fieldInfos);
        while(type.BaseType != typeof(object)) {
            type = type.BaseType;
            fieldInfos = type.GetFields(bindingFlags);

            // Look for fields we do not have listed yet and merge them into the main list
            for(int index = 0; index < fieldInfos.Length; ++index) {
                bool found = false;

                for(int searchIndex = 0; searchIndex < fieldInfoList.Count; ++searchIndex) {
                    bool match =
                        (fieldInfoList[searchIndex].DeclaringType == fieldInfos[index].DeclaringType) &&
                        (fieldInfoList[searchIndex].Name == fieldInfos[index].Name);

                    if(match) {
                        found = true;
                        break;
                    }
                }

                if(!found) {
                    fieldInfoList.Add(fieldInfos[index]);
                }
            }
        }

        return fieldInfoList.ToArray();
    }
}

Be aware that I'm manually comparing the fields in a nested for loop. If you have deeply nested classes or monstrously big classes, feel free to use a HashSet<> instead.

EDIT: Also be aware that this doesn't search types further down in the inheritance chain. In my case I know that I'm at the most derived type when callling the method.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!