عدم الحصول على حقول من GetType (). getfields مع bindingflag.default

StackOverflow https://stackoverflow.com/questions/1155529

  •  18-09-2019
  •  | 
  •  

سؤال

أنا أستخدم فئات الانعكاس من أجل الحصول على جميع الحقول داخل كائن معين. ومع ذلك، فإن مشكلتي هي أنه يعمل تماما عندما تكون الحقول داخل فئة طبيعية، مثل:

class test
{
   string test1 = string.Empty;
   string test2 = string.Empty;
}

هنا أحصل على كل من Test1 و Test2، مشكلتي هي أنني أستخدم التجريد وبالتالي عدة فصول مجتمعة.

حصلت على شيء مثل:

class test3 : test2
{
   string test4 = string.Empty;
   string test5 = string.Empty;
}

class test2 : test1
{
   string test2 = string.Empty;
   string test3 = string.Empty;
}
class test1
{
   string test0 = string.Empty;
   string test1 = string.Empty;
}

ولكن عندما أجريها، لا أحصل على الحقول مرة أخرى من GetType().GetFields(BindingFlag.Default).

لدى كل من هذه الحقول أيضا ممتلكات، get; set; أضفها له. عندما أقوم بتشغيل التعليمات البرمجية، أحصل على الخصائص مرة أخرى إلى Test1 ولكن ليس الحقول الفعلية.

هذا هو الكود الذي أحاول الحصول على الحقول مع:

FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Default);
foreach (FieldInfo field in fields)

لقد حاولت أيضا:

FieldInfo[] fields = Obj.GetType().GetFields(BindingFlags.Public 
                                             | BindingFlags.Instance 
                                             | BindingFlags.NonPublic 
                                             | BindingFlags.Static);

يمكنني استخدام نفس الرمز للخصائص:

PropertyInfo[] properties = Obj.GetType().GetProperties(BindingFlags.Public 
                                             | BindingFlags.Instance 
                                             | BindingFlags.NonPublic 
                                             | BindingFlags.Static);

foreach (PropertyInfo property in properties)

أي أفكار لماذا أحصل على الممتلكات من الفئات المستخرجة ولكن ليس الحقول؟

هل كانت مفيدة؟

المحلول

تحرير: للحصول على نشر أعضاء من النوع الأساسي، يجب عليك:

typeof(T).BaseType.GetFields(...)

تحرير مرة أخرى: الفوز.

تحرير 3/22/13: المستخدمة Concat بدلا من Union. وبعد لأننا نحدد BindingFlags.DeclaredOnly ونوع BaseType لا يمكن أن يساوي نفسه، Union ليست هناك حاجة وهي أكثر تكلفة.

public static IEnumerable<FieldInfo> GetAllFields(Type t)
{
    if (t == null)
        return Enumerable.Empty<FieldInfo>();

    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | 
                         BindingFlags.Static | BindingFlags.Instance | 
                         BindingFlags.DeclaredOnly;
    return t.GetFields(flags).Concat(GetAllFields(t.BaseType));
}

نصائح أخرى

لا يمكن للنوع الذي يرث نوعا آخر رؤية أجزاء خاصة من هذا النوع الآخر، فيمكنه رؤية الأجزاء المحمية والداخلية والعامة. النظر في التعليمات البرمجية التالية:

class A
{
    // note that this field is private
    string PrivateString = string.Empty;
    // protected field
    protected string ProtectedString = string.Empty;
}

class B : A { }

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("B Fields:");
        B b = new B();
        b.GetType()
            .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .ToList()
            .ForEach(f => Console.WriteLine(f.Name));

        Console.WriteLine("A Fields:");
        A a = new A();
        a.GetType()
            .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
            .ToList()
            .ForEach(f => Console.WriteLine(f.Name));

    }
}

إخراج هذا البرنامج هو ما يلي:

B Fields:
ProtectedString
A Fields:
PrivateString
ProtectedString

لذلك، النوع A لديه حقلين؛ PrivateString و ProtectedString. وبعد اكتب B لديه واحد؛ ProtectedString, ، أن يرث من A. وبعد إذا كنت ترغب في "الوصول" PrivateString من خلال النوع B, ، سوف تحتاج إلى الانتقال إلى نوع الأساس (b.GetType().BaseType).

ملاحظة رغم ذلك، حتى لو كان النوع B تقارير أن يكون لديك حقل يسمى ProtectedString, ، لا يزال هذا الحقل غير معلن B; ؛ أعلن في A. وبعد يمكن فحص هذا عن طريق إضافة BindingFlags.DeclaredOnly إلى GetFields يدعو في برنامج العينة أعلاه؛ GetFields سوف ترجع أي مجالات ل B, ، واثنان ل A.

ترجم إلى نموذج الكود الخاص بك، وهذا يعني أن النوع test3 لا يحتوي على الحقول test2 و test3, ، لأنها خاصة إلى النوع test2 (تشابه أسماء الحقول ونوع الأسماء تجعل هذه الجملة مربكة إلى حد ما، أخشى).

يمكنك استخدام طريقة التمديد هذه لإعادة تكرار التسلسل الهرمي للميراث من النوع، وكل الطريق إلى الكائن، وإرجاع جميع مجالات النوع وجميع أسلافها بشكل فعال:

public static class ReflectionExtensions
{
    public static IList<FieldInfo> GetAllFields(this Type type, BindingFlags flags)
    {
        if(type == typeof(Object)) return new List<FieldInfo>();

        var list = type.BaseType.GetAllFields(flags);
        // in order to avoid duplicates, force BindingFlags.DeclaredOnly
        list.AddRange(type.GetFields(flags | BindingFlags.DeclaredOnly));
        return list;
    }
}

(غير مختبر، YMMV)

الخصائص موروثة، الحقول ليست كذلك. الحقول المحمية مرئية لفصول الدرسات، ولكن لا تورثها. وبعبارة أخرى، فإن فئة SLECALDY لديها بالفعل خصائص فئةها الأساسية، لكنها مجرد قادرة على رؤية الحقول.

إذا كنت تريد فقط الأسماء لكل من الخصائص والحقول، استخدم

private static IEnumerable<string > GetAllFieldsAndProperties(Type t)
{
  if (t == null)
    return Enumerable.Empty<string>();

  BindingFlags flags = BindingFlags.Public 
    | BindingFlags.NonPublic 
    | BindingFlags.Static 
    | BindingFlags.Instance 
    | BindingFlags.DeclaredOnly;
  return t.GetFields(flags).Select(x=>x.Name)
    .Union(GetAllFieldsAndProperties(t.BaseType))
    .Union(t.GetProperties(flags).Select(x=>x.Name));
}

تعداد جميع الحقول من النوع بما في ذلك الأعضاء الخاصين من الفصول الأساسية.

public static IEnumerable<FieldInfo> EnumerateFields(this Type type, BindingFlags flags) =>
   type.BaseType?.EnumerateFields(flags)
       .Concat(type.GetFields(flags | BindingFlags.DeclaredOnly)) ??
   type.EnumerateFields(flags);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top