2012-02-08 9 views
7

, 이는 파생 클래스에서) 모든 명시 적으로 선언 필드를를 반환합니다, b)는 모든 파생 클래스에서 자동 속성 백업 필드 및 c) 모든 명시 적으로 선언 필드 기본 클래스에서. Type.GetFields()가 기본 클래스의 보조 필드를 반환하지 않는 이유는 무엇입니까? 당신이 파생 클래스를 나타내는 유형 <code>Type.GetFields()</code>를 사용하는 경우 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); 
     } 
    } 
} 

이 바,하지 푸의 후원 필드를 표시합니다.

답변

8

뒷받침 필드 인 필드는 반사에 영향을주지 않습니다. 뒷받침 필드의 유일한 관련 속성은 개인 필드라는 것입니다.

FlattenHierarchy을 사용하는 경우에도 리플렉션 함수는 전용 전용 클래스 멤버를 반환하지 않습니다. 클래스 계층 구조를 통해 수동으로 루프를 실행하고 각각에 대해 비공개 필드를 요청해야합니다.

내가 생각 FlattenHierarchy는 당신이 보는 클래스 코드에 대한 모든 구성원이 볼 보여줄 의도로 기록됩니다. 따라서 기본 멤버는 더 파생 된 클래스에서 동일한 이름을 가진 멤버에 의해 숨겨 지거나 섀도 잉 될 수 있으며 private 멤버는 전혀 표시되지 않습니다.

+0

FlattenHierarchy는 다음과 같은 의견을 가지고 : 여기 – R2D2

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

중첩 for 루프의 필드를 수동으로 비교한다는 점에 유의하십시오. 깊게 중첩 된 클래스 나 대단히 큰 클래스가있는 경우 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(); 
    } 
} 
5

는 HashSet의를 사용하여 수정 된 버전입니다. 상속 된 클래스의 private 정적 멤버는 반환되지 않습니다. 정적 구성원은 필드, 메서드, 이벤트 및 속성을 포함합니다. 중첩 형식은 반환되지 않습니다. 여기 static이라는 단어가 언급됩니다. static 멤버가 없을 때 작동하지 않는다고 생각하게합니다.
+0

"Cygon의 샘플 함수는 클래스에 기본 클래스가 있다면 오브젝트의 기본 필드 만 검색합니다! = object." - 네가하려는 말을 못 들었어. 'List '을'HashSet '처럼 시작 유형의 필드로 초기화 했으므로 두 솔루션 모두 시작 유형의 필드도 포함합니다. – Cygon

+0

그렇지 않으면 멋지게 끝내고, 특히'UnionWith()'를 사용하면 배열 스캔보다 훨씬 더 우아합니다. 퍼포먼스가 좋으면'HashSet'은별로 도움이되지 않는 것처럼 보입니다. 7157ms (List)와 7160ms (HashSet)에서 끝나는 1,000,000 번의 반복을 위해 3 단계의 상속 수준에서 30 개의 필드를 사용하여 시도했습니다. – Cygon

+0

혼란에 빠져서 죄송합니다. 그 한계를 가지고있는 중간 구현 일 수도 있었고 나는 그것이 당신 것이라고 생각했습니다. 위의 텍스트에서 내 진술을 삭제했습니다. – Piper