GetSerializableMembers(FormatterServices)返回相同的字段两次!为什么?

时间:2013-03-01 20:31:38

标签: c# serialization reflection

FormatterServices.GetSerializableMembers两次为派生类型返回受保护和内部字段。一次作为SerializationFieldInfo的实例,一次作为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。但是这是运行程序时的实际输出:

DerivedA
  BaseA+privateField (67108865)

DerivedB
  protectedField (67108866)
  BaseB+protectedField (67108866)

正如您所看到的,protectedField出现两次,名称不同但具有相同的元数据标记,因此确实是同一个字段。

任何人都可以解释原因吗?

2 个答案:

答案 0 :(得分:1)

这似乎与FormatterServices没有多大关系,而是与反射如何工作以及FormatterServices如何使用它有关。对于与Type.GetFields一起使用的BindingFlags.NonPublic方法(请参阅:http://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进行过滤:

希望这有帮助。

答案 1 :(得分:0)

从几个角度测试之后,我决定改变我的答案。

GetSerializableMembers()方法缺失, 重复项不是底层内存的正确投影。 (这真的很令人惊讶..)

我建议使用: t.GetType()。GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

检查成员列表的结果。

祝你好运。