2012-02-08 28 views
7

Trong C#, nếu bạn sử dụng Type.GetFields() với kiểu đại diện cho lớp dẫn xuất, nó sẽ trả về a) tất cả các trường khai báo rõ ràng trong lớp dẫn xuất, b) tất cả các trường sao lưu của thuộc tính tự động trong lớp dẫn xuất và c) tất cả các trường được khai báo rõ ràng trong lớp cơ sở.Tại sao không Type.GetFields() trả về các trường sao lưu trong một lớp cơ sở?

Tại sao d) trường sao lưu thuộc tính tự động trong lớp cơ sở bị thiếu?

Ví 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); 
     } 
    } 
} 

này sẽ chỉ hiển thị các lĩnh vực sao lưu của Bar, không Foo.

Trả lời

8

Trường là trường sao lưu không có ảnh hưởng đến sự phản chiếu. Thuộc tính duy nhất có liên quan của các trường sao lưu là chúng là riêng tư.

Chức năng phản chiếu không trả về riêng thành viên của các lớp cơ sở, ngay cả khi bạn sử dụng FlattenHierarchy. Bạn sẽ cần lặp vòng theo cách thủ công qua phân cấp lớp của bạn và yêu cầu các trường riêng tư trên từng phân cấp.

Tôi nghĩ rằng FlattenHierarchy được viết với ý định hiển thị tất cả các thành viên hiển thị với mã trong lớp bạn xem. Vì vậy, các thành viên cơ sở có thể được ẩn/che giấu bởi các thành viên có cùng tên trong một lớp học có nguồn gốc nhiều hơn và các thành viên riêng tư là không thể nhìn thấy ở tất cả.

+0

FlattenHierarchy Có những bình luận sau Chỉ định rằng cộng đồng và bảo vệ các thành viên tĩnh lên hệ thống phân cấp nên được trả lại. Các thành viên tĩnh riêng trong các lớp kế thừa không được trả về. Tĩnh thành viên bao gồm các trường, phương thức, sự kiện và thuộc tính. Các loại lồng nhau không được trả lại. Nó đề cập đến từ tĩnh ở đây, mà làm cho tôi nghĩ rằng nó sẽ không làm việc cho không có thành viên tĩnh – R2D2

1

Cảm ơn bạn đã đến @CodeInChaos để có câu trả lời nhanh chóng và đầy đủ!

Trong trường hợp bất kỳ ai khác tình cờ gặp vấn đề này, đây là giải pháp nhanh chóng theo các trường đến lớp cơ sở xa nhất.

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

Lưu ý rằng tôi đang so sánh thủ công các trường trong vòng lặp lồng nhau. Nếu bạn có các lớp lồng nhau sâu sắc hoặc các lớp học khổng lồ, hãy sử dụng HashSet <> để thay thế.

EDIT: Cũng lưu ý rằng điều này không tìm kiếm các loại tiếp tục xuống trong chuỗi kế thừa. Trong trường hợp của tôi, tôi biết rằng tôi đang ở loại có nguồn gốc cao nhất khi gọi phương thức.

5

Đây là một phiên bản sửa đổi sử dụng 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

"Chức năng mẫu của Cygon chỉ lấy trường của lớp cơ sở nếu lớp có lớp cơ sở! = Đối tượng." - Tôi không hiểu anh đang nói gì. Tôi đã khởi tạo 'Danh sách ' của mình với các trường từ loại bắt đầu, giống như bạn thực hiện 'HashSet ', vì vậy cả hai giải pháp sẽ chứa các trường của loại bắt đầu. – Cygon

+0

Nếu không, thực hiện tốt, đặc biệt là bạn sử dụng 'UnionWith()', thanh lịch hơn nhiều so với quét mảng của tôi. Hiệu suất khôn ngoan, 'HashSet' dường như không làm được gì nhiều, tôi đã thử nó với 30 trường trong 3 cấp độ thừa kế cho 1.000.000 lần lặp, kết thúc trong 7157 ms (Danh sách) so với 7160 ms (HashSet). – Cygon

+0

Xin lỗi về sự nhầm lẫn, bạn đã đúng. Nó có thể là một thực hiện trung gian có giới hạn đó và tôi cho rằng đó là của bạn. Tôi đã xóa tuyên bố của mình trong văn bản ở trên. – Piper

Các vấn đề liên quan