2013-03-01 9 views
8

FormatterServices.GetSerializableMembers zwraca dwukrotnie zabezpieczone i wewnętrzne pola dla typów pochodnych. Raz jako instancja SerializationFieldInfo i raz jako RtFieldInfo.GetSerializableMembers (FormatterServices) zwraca dwukrotnie to samo pole! Czemu?

Uważam, że jest to bardzo mylące! Czy ktoś może mi pomóc zrozumieć, dlaczego Microsoft zdecydował się wdrożyć go w ten sposób?

Pisałem przykładowy program, który ponownie produkować mój problem:

class Program 
{ 
    [Serializable] 
    public class BaseA 
    { 
     private int privateField; 
    } 

    [Serializable] 
    public class DerivedA : BaseA { } 

    [Serializable] 
    public class BaseB 
    { 
     protected int protectedField; 
    } 

    [Serializable] 
    public class DerivedB : BaseB { } 

    static void Main(string[] args) 
    { 
     Program.PrintMemberInfo(typeof(DerivedA)); 
     Program.PrintMemberInfo(typeof(DerivedB)); 
     Console.ReadKey(); 
    } 

    static void PrintMemberInfo(Type t) 
    { 
     Console.WriteLine(t.Name); 

     foreach (var mbr in FormatterServices.GetSerializableMembers(t)) 
     { 
      Console.WriteLine(" {0} ({1})", mbr.Name, mbr.MetadataToken); 
     } 

     Console.WriteLine(); 
    } 
} 

Spodziewam się, że privateField i protectedField podano raz każda. Jednak jest to rzeczywista moc przy uruchamianiu programu:

 
DerivedA 
    BaseA+privateField (67108865) 

DerivedB 
    protectedField (67108866) 
    BaseB+protectedField (67108866) 

Jak widać protectedField pojawiają się dwa razy, z różnymi nazwami, ale z tego samego metadanych żeton więc jest to rzeczywiście ten sam obszar.

Czy ktoś może wyjaśnić, dlaczego?

+0

Podobno jest to znane od pewnego czasu: http://msdn.microsoft.com/en-us/library/2bb1dc1s(v=vs.90).aspx (sekcja komentarzy). –

+0

Ale nadal nie ma wyjaśnienia ... :-( –

Odpowiedz

1

Wydaje się, że nie ma to tak dużego związku z usługami FormatterServices, ale z tym, jak działa odbicie i jak jest używane przez FormatterServices. Dla metody Type.GetFields, gdy jest używana z BindingFlags.NonPublic (patrz: http://msdn.microsoft.com/en-us/library/6ztex2dc.aspx): "Zwracane są tylko chronione i wewnętrzne pola na klasach bazowych, prywatne pola w klasach bazowych nie są zwracane."

całkowicie pozbawiony wszelkich kontroli i dostosowane do Twojego przykład, co FormatterServices robi, aby uzyskać pól jest w zasadzie:

static IEnumerable<FieldInfo> GetSerializableFields(Type type, Func<Type, IEnumerable<FieldInfo>> andNext) 
    { 
     return 
      (type.IsInterface || type == typeof(object)) 
      ? new FieldInfo[0] 
      : type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) 
        .Where(f => (f.Attributes & FieldAttributes.NotSerialized) != FieldAttributes.NotSerialized) 
        .Concat(andNext(type)); 
    } 

    static void PrintMemberInfo(Type t) 
    { 
     Console.WriteLine(t.Name); 

     Func<Type, IEnumerable<FieldInfo>> andNext = null; 
     andNext = tp => GetSerializableFields(tp.BaseType, andNext); 
     var fields = GetSerializableFields(t, tp => new FieldInfo[0]).ToArray(); 
     var base_fields = GetSerializableFields(t.BaseType, andNext).ToArray(); 

     var counter = 0; 
     foreach (var f in fields.Concat(base_fields)) 
     { 
      Console.WriteLine(
       "{0} Reflected: {1} - Declaring: {2} - Field: {3} ({4})", 
       (counter++) + 1, f.ReflectedType.Name, f.DeclaringType.Name, f.Name, f.MetadataToken); 
     } 
     Console.WriteLine(); 
    } 
} 

który generuje następujący wynik dla swoich przykładowych klas:

DerivedA 
1 Reflected: BaseA - Declaring: BaseA - Field: privateField (67108865) 

DerivedB 
1 Reflected: DerivedB - Declaring: BaseB - Field: protectedField (67108866) 
2 Reflected: BaseB - Declaring: BaseB - Field: protectedField (67108866) 

Natomiast FormatterServices po prostu nie filtruje swoich wyników, sprawdzając, czy zawiera to samo pole z tego samego typu deklarowania więcej niż raz. Biorąc pod uwagę sposób, w jaki jest implementowana funkcja FormatterServices (przeprowadzanie sprawdzania typów bazowych typów), prawdopodobnie powinny one wykonywać filtr podobny do ReflectedType == DeclaringType:

Mam nadzieję, że to pomoże.

+0

Tak, to tak, jakby zapomnieli zrobić to filtrowanie. Dziękuję za wysiłek badawczy. –

0

Po przetestowaniu go pod kilkoma kątami, postanowiłem zmienić odpowiedź.

Metoda GetSerializableMembers() jest uszkodzona, Duplikaty nie są poprawną projekcją podstawowej pamięci. (co jest naprawdę zaskakujące ..)

Proponuję za pomocą: t.GetType() GetMembers (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).

I sprawdź wynik nadchodzącej listy członków.

Powodzenia.

+0

Dziękuję za odpowiedź, ale piszesz "... uzyskaj wszystkie wyprowadzone elementy obiektu, które są oznaczone [Serializowalne], niezależnie od ich modyfikatora dostępu."- Sposób, w jaki interpretuję to, co faktycznie przemawia przeciwko temu, co zwraca metoda, gdy to samo pole jest zwracane więcej niż raz Nie mogę wymyślić przypadku użycia pojedynczego duplikatu, który jest interesujący lub niezrozumiały. może dostarczyć taki przykład: –

+0

Wystarczająco sprawiedliwe, dodałem taki przykład, ale przykład lub użycie przypadku nie jest tak naprawdę problemem pod ręką - pytanie brzmi: wolałbyś raczej metodę zwracania wyników, które są odpowiednie do tego, jak my programiści lub ludzie wizualizują obiekt lub jak jest on fizycznie przechowywany w pamięci? –

+0

Myślę, że brakuje ci punktu w moim pytaniu. Pola, które uważam za duplikaty, odnoszą się do tego samego członka, ponieważ mają identyczne tokeny metadanych. wysłałeś, że masz dwa oddzielne pola (różne tokeny metadanych), więc nie ma tam duplikatów! –

Powiązane problemy