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

Mår*_*röm 8 c# reflection serialization

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望这一点,privateFieldprotectedField报告一次.但是这是运行程序时的实际输出:

DerivedA
  BaseA+privateField (67108865)

DerivedB
  protectedField (67108866)
  BaseB+protectedField (67108866)

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

有谁能解释为什么?

Ale*_*lex 1

这似乎与 FormatterServices 没有太大关系,而是与反射如何工作以及 FormatterServices 如何使用它有关。对于Type.GetFields使用 with 时的方法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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它为您的示例类生成以下输出:

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)
Run Code Online (Sandbox Code Playgroud)

FormatterServices 根本不会通过检查是否多次包含同一声明类型的相同字段来过滤其结果。鉴于 FormatterServices 的实现方式(对类型的可序列化基类型执行检查),它们可能应该执行类似通过 ReflectedType == DeclaringType 进行过滤的操作:

希望这可以帮助。