2012-02-08 70 views
7

在C#中,如果使用代表派生類的類型Type.GetFields(),它將返回a)派生類中所有顯式聲明的字段,b)派生類中自動屬性的所有後備字段以及c)所有顯式聲明的字段在基類中。爲什麼Type.GetFields()返回基類中的後臺字段?

爲什麼缺少基類中自動屬性的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); 
     } 
    } 
} 

這將只顯示欄上,沒有富的支持字段。

回答

8

作爲背景場的場對反射沒有影響。支持域的唯一相關屬性是它們是私有的。

反射函數不返回私有基類的成員,即使您使用FlattenHierarchy。您需要在您的類層次結構上手動循環,並要求每個專用字段。

我認爲FlattenHierarchy是爲了顯示所有成員在您看到的類中的代碼可見而編寫的。因此,基礎成員可以被更多派生類中的具有相同名稱的成員隱藏/遮蔽,並且私有成員根本不可見。

+0

FlattenHierarchy有以下評論指定公共和保護的靜態成員了層次結構應 返回。不會返回繼承類中的私有靜態成員。靜態 成員包括字段,方法,事件和屬性。嵌套類型不返回。它提到static這個詞,這讓我認爲它不適用於任何靜態成員 – R2D2 2018-01-18 11:36:27

1

感謝轉到@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(); 
    } 
} 

請注意,我手動比較了嵌套循環中的字段。如果你有深層次的嵌套類或怪異的大類,可以隨意使用HashSet代替。

編輯:也請注意,這不會在繼承鏈中進一步搜索類型。在我的情況下,我知道我在調用方法時處於派生類型最多的狀態。

5

下面是一個使用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(); 
    } 
} 
+0

「如果類有基類!=對象,Cygon的示例函數僅檢索基類的字段。」 - 我不明白你想說什麼。我使用起始類型的字段初始化我的'List ',就像你做'HashSet '一樣,所以這兩個解決方案也包含起始類型的字段。 – Cygon 2012-09-08 13:18:19

+0

否則,很好地完成,尤其是你使用'UnionWith()',比我的數組掃描更優雅。性能方面,'HashSet'似乎沒有太大的作用,我用3個繼承級別的30個字段進行了1,000,000次迭代嘗試,結束於7157 ms(List)vs 7160 ms(HashSet)。 – Cygon 2012-09-08 13:26:23

+0

對不起,你說得對。它可能是一箇中間實現,有這種限制,我認爲它是你的。我在上面的文字中刪除了我的陳述。 – Piper 2012-09-10 14:06:44

相關問題