8

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

我会预料到这一点,privateField并且protectedField每次都被报告一次。然而,这是运行程序时的实际输出:

衍生A
  BaseA+privateField (67108865)

衍生B
  受保护字段 (67108866)
  BaseB+protectedField (67108866)

如您所见protectedField,出现两次,名称不同但元数据标记相同,因此它确实是同一个字段。

谁能解释为什么?

4

2 回答 2

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 的实现方式(对类型的可序列化基类型执行检查),它们可能应该执行类似 Filter by 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 回答