2013-03-01 7 views
8

FormatterServices.GetSerializableMembersは、派生型に対してprotectedフィールドとinternalフィールドを2回返します。いったんSerializationFieldInfoのインスタンスとして1回、RtFieldInfoとして1回。GetSerializableMembers(FormatterServices)は同じフィールドを2回返します。どうして?

私はこれが非常に混乱していることを発見しました!マイクロソフトがなぜこのように実装することにしたのか誰でも理解できますか?

私は私の問題を再生産するサンプルプログラムを書かれている:

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

私はprivateFieldprotectedFieldは、一度、各報告されることを期待します。

 
DerivedA 
    BaseA+privateField (67108865) 

DerivedB 
    protectedField (67108866) 
    BaseB+protectedField (67108866) 

あなたはprotectedFieldは異なる名前で同じメタデータを、2回表示さ見ることができるようにトークンので、それは確かに非常に同じフィールドである。しかし、これは、プログラムを実行している実際の出力です。

誰でも理由を説明できますか?

+0

これはしばらくの間知られていました。http://msdn.microsoft.com/en-us/library/2bb1dc1s(v=vs.90).aspx(コメントセクション) –

+0

しかし、まだ説明はありません。:-( –

答えて

1

これはFormatterServicesとはあまり関係がないようですが、反射がどのように作用し、FormatterServicesによってどのように使用されるかに関係しています。 BindingFlags.NonPublichttp://msdn.microsoft.com/en-us/library/6ztex2dc.aspx参照)と一緒に使用した場合のType.GetFieldsメソッドの場合: "ベースクラスの保護された内部フィールドのみが返され、ベースクラスのプライベートフィールドは返されません。"

が完全に任意の小切手を剥奪され、あなたの例に合わせて、フィールドを取得するために何をするかFormatterServices基本的には次のとおりです。

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) 

:あなたの例クラスの次の出力を生成します

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

また、FormatterServicesは、同じ宣言型の同じフィールドが複数回含まれているかどうかをチェックすることで、結果をフィルタリングしません。 FormatterServicesが実装されている(型のシリアライズ可能な基本型のチェックを実行する)方法を考えると、おそらくFilterのようなものです。ReflectedType == DeclaringType:

+0

はい、それはそのフィルタリングを忘れたのと同じです。あなたの研究努力をありがとう。 –

0

私はいくつかの角度からテストした後、私の答えを変えることにしました。

GetSerializableMembers()メソッドが異常終了しました。 重複は、基になるメモリの正しい投影ではありません。 (本当に驚くべきことである。)

私が使用することをお勧め: t.GetTypeを()GetMembersを(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)。

メンバーリストの結果を確認してください。

Good Luck。

+0

回答ありがとうございます。ただし、 "...アクセス修飾子に関係なく[Serializable]とマークされた派生オブジェクトメンバーをすべて取得します。" - 私が解釈する方法は、同じフィールドが複数回返される場合にメソッドが返すものに対して実際に話します。重複が面白い、または混乱しないというユースケースを考えることはできません。 –

+0

私はそのような例を追加しましたが、例または事例の使用は実際には問題ではありません。問題は次のとおりです。開発者や人間はオブジェクトをどのように視覚化するのか、それとも物理的にどのようにメモリに格納されているのでしょうか? –

+0

私はあなたが私の質問でポイントを逃していると思います。あなたは2つの別々のフィールド(異なるメタデータトークン)を持っているので、そこに重複はありません。 –

関連する問題