2013-03-01 52 views
8

FormatterServices.GetSerializableMembers返回派生类型的protected和internal字段两次。一次作为SerializationFieldInfo的实例,一次作为RtFieldInfoGetSerializableMembers(FormatterServices)两次返回相同的字段!为什么?

我觉得这很混乱!任何人都可以帮助我理解为什么微软决定以这种方式实现它?

我写了重新制作我的问题的一个范例程序:

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的每一次报道。然而,这是运行程序时的实际输出:正如你所看到protectedField出现两次,名称不同但具有相同的元数据

 
DerivedA 
    BaseA+privateField (67108865) 

DerivedB 
    protectedField (67108866) 
    BaseB+protectedField (67108866) 

令牌,因此它确实是非常相同的领域。

任何人都可以解释为什么吗?

+0

显然,这已经知道了一段时间:http://msdn.microsoft.com/en-us/library/2bb1dc1s(v=vs.90).aspx(评论部分)。 – 2013-03-01 23:30:05

+0

但是仍然没有解释...... :-( – 2013-03-02 14:46:54

回答

1

这似乎与FormatterServices无关,但与FormatterServices如何使用反射以及如何使用它有关。与BindingFlags.NonPublic(参见:http://msdn.microsoft.com/en-us/library/6ztex2dc.aspx)一起使用时的Type.GetFields方法:“只返回基类上的受保护和内部字段;不返回基类上的私有字段。”

完全剥离的任何检查,并根据自己的例子,什么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 ==

希望这有所帮助。

+0

是的,就像他们忘了做那个过滤一样。感谢您的研究努力。 – 2013-07-14 12:29:13

0

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

GetSerializableMembers()方法有缺陷, 重复项不是底层内存的正确投影。 (这真是奇了..)

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

然后检查成员列表的包含结果。

祝你好运。

+0

感谢您的回答。然而,您编写了“...获取所有标记为[Serializable]的派生对象成员,而不管其访问修饰符如何。“ - 我解释的方式实际上反驳了方法在同一个字段不止一次返回的情况下所返回的结果,我想不出一个重复的有趣或非混淆的用例,如果你愿意,可以提供这样一个例子 – 2013-04-06 15:56:38

+0

公平的是,我添加了这样的一个例子,但是这个例子或案例的用法实际上并不是真正的问题 - 问题是:你宁愿选择返回适合我们的结果的方法开发人员或人类可视化对象或它是如何物理存储在内存中的? – 2013-04-07 08:20:26

+0

我认为你错过了我的问题的重点,我认为重复的字段实际上是指同一成员,因为它们具有相同的元数据标记。你发布了你有两个单独的字段(不同的元数据标记),所以没有重复! – 2013-04-07 21:18:00

相关问题