8

FormatterServices.GetSerializableMembersは、派生型の保護フィールドと内部フィールドを2回返します。のインスタンスとしてSerializationFieldInfo1回、。として1回RtFieldInfo

これは非常に紛らわしいと思います!マイクロソフトがこの方法で実装することを決定した理由を誰かが理解するのを手伝ってもらえますか?

問題を再現するサンプルプログラムを作成しました。

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それぞれ1回報告されます。ただし、これはプログラム実行時の実際の出力です。

DerivedA
  BaseA + privateField(67108865)

DerivedB
  protectedField(67108866)
  BaseB + protectedField(67108866)

ご覧のとおりprotectedField、2回表示されますが、名前は異なりますが、メタデータトークンが同じであるため、実際にはまったく同じフィールドです。

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

4

2 に答える 2

1

これは、FormatterServicesにはあまり関係がないようですが、リフレクションがどのように機能し、FormatterServicesによってどのように使用されるかに関係しています。で使用する場合のType.GetFieldsメソッドの場合BindingFlags.NonPublichttp://msdn.microsoft.com/en-us/library/6ztex2dc.aspxを参照):「基本クラスの保護フィールドと内部フィールドのみが返されます。基本クラスのプライベートフィールドは返されません。 。」

チェックを完全に取り除き、例に合わせて、FormatterServicesがフィールドを取得するために行うことは基本的に次のとおりです。

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

これにより、サンプルクラスに対して次の出力が生成されます。

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)

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

お役に立てれば。

于 2013-07-14T00:20:12.070 に答える
0

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

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

t.GetType()。GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);を使用することをお勧めします。

そして、メンバーのリストについては、受信結果を確認してください。

幸運を。

于 2013-04-04T06:54:07.760 に答える