为啥 Type.GetFields() 不返回基类中的支持字段?

Posted

技术标签:

【中文标题】为啥 Type.GetFields() 不返回基类中的支持字段?【英文标题】:Why doesn't Type.GetFields() return backing fields in a base class?为什么 Type.GetFields() 不返回基类中的支持字段? 【发布时间】:2012-03-01 09:24:49 【问题描述】:

在 C# 中,如果将 Type.GetFields() 与表示派生类的类型一起使用,它将返回 a) 派生类中所有显式声明的字段,b) 派生类中自动属性的所有支持字段,以及 c) 所有在基类中显式声明的字段。

为什么缺少基类中自动属性的 d) 支持字段?

例子:

public class Base 
    public int Foo  get; set; 

public class Derived : Base 
    public int Bar  get; set; 

class Program 
    static void Main(string[] args) 
        FieldInfo[] fieldInfos = typeof(Derived).GetFields(
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.FlattenHierarchy
        );
        foreach(FieldInfo fieldInfo in fieldInfos) 
            Console.WriteLine(fieldInfo.Name);
        
    

这将只显示 Bar 的支持字段,而不是 Foo。

【问题讨论】:

【参考方案1】:

作为后备字段的字段对反射没有影响。支持字段的唯一相关属性是它们是私有的。

反射函数不返回基类的私有成员,即使您使用FlattenHierarchy。您将需要手动遍历您的类层次结构,并要求每个类的私有字段。

我认为FlattenHierarchy 的编写目的是显示所有成员对您查看的类中的代码可见。因此,基成员可以被派生类中具有相同名称的成员隐藏/隐藏,而私有成员则根本不可见。

【讨论】:

FlattenHierarchy 具有以下注释 指定应返回层次结构上的公共和受保护静态成员。不返回继承类中的私有静态成员。静态成员包括字段、方法、事件和属性。不返回嵌套类型。它在这里提到了静态这个词,这让我觉得它不适用于非静态成员【参考方案2】:

这是一个使用 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();
    

【讨论】:

"Cygon 的示例函数仅在类具有基类 != 对象时才检索基类的字段。" - 我不明白你想说什么。我用起始类型的字段初始化了我的List&lt;FieldInfo&gt;,就像你做HashSet&lt;FieldInfo&gt;一样,所以两个解决方案也将包含起始类型的字段。 否则,做得很好,尤其是你使用UnionWith(),比我的数组扫描优雅得多。性能方面,HashSet 似乎没有多大作用,我尝试了 30 个字段在 3 个继承级别进行 1,000,000 次迭代,完成时间为 7157 毫秒(列表)和 7160 毫秒(哈希集)。 对不起,您是对的。它可能是一个具有这种限制的中间实现,我认为它是你的。我在上面的文字中删除了我的陈述。【参考方案3】:

感谢@CodeInChaos 快速而完整的回答!

如果其他人偶然发现这一点,这里有一个快速的解决方法,可以跟踪字段直到最远的基类。

/// <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();
    

请注意,我正在手动比较嵌套 for 循环中的字段。如果您有深度嵌套的类或非常大的类,请随意使用 HashSet。

编辑:还要注意,这不会在继承链中进一步搜索类型。就我而言,我知道在调用该方法时我处于最派生的类型。

【讨论】:

以上是关于为啥 Type.GetFields() 不返回基类中的支持字段?的主要内容,如果未能解决你的问题,请参考以下文章

Apollo Graphql 自定义标量 JSON - 抱怨“TypeError:type.getFields 不是函数”

为啥当从派生类访问基类的数据时它会返回废话(我认为是指针数据)

为啥不抽象字段?

为啥 C# 中的基类允许实现接口契约而不继承它?

为啥我的类不继承其基类中定义的方法?

为啥基类方法不尊重 new 关键字?