使用Reflection确定哪些字段是属性的后备字段

我正在使用reflection来绘制对象。 这些对象在托管代码中,但除了通过reflection之外,我无法看到它们的源代码,底层结构等。 所有这一切的首要目标是一个对象的基本内存映射(类似于SOS.dll DumpObject!ObjSize命令的function)。 因此,我试图确定哪些成员被“双重计算”为一个字段和一个属性。

例如:

 public class CalendarEntry { // private property private DateTime date { get; set;} // public field public string day = "DAY"; } 

映射时显示:

  • 字段
    • k__BackingField
  • 属性
    • 日期

像这样的一个类:

 public class CalendarEntry { // private field private DateTime date; // public field public string day = "DAY"; // Public property exposes date field safely. public DateTime Date { get { return date; } set { date = value; } } } 

映射时显示:

  • 字段
    • 日期
  • 属性
    • 日期

乍一看没有什么可以告诉你Date属性的“支持字段”是名为date的字段。 我试图避免在这种情况下计算两次日期,因为这会给我一个糟糕的内存大小近似值。

更令人困惑/复杂的是我遇到过这样的场景,其中属性并不总是具有将通过Type.GetFields()方法列出的相应字段,因此我不能完全忽略所有属性。

关于如何确定从Type.GetFields()返回的集合中的字段是否实际上是从Type.GetProperties()返回的某些相应属性的支持字段的任何想法?

编辑 – 我无法确定属性在Type.GetFields()返回的集合中列出的相应字段的条件。 有人熟悉这种情况吗?

编辑2-我找到了一个很好的例子,说明属性的支持字段何时不会包含在从Type.GetFields()返回的集合中。 在String的引擎盖下查看时,您有以下内容:

  • Object包含名为FirstChar的Property
  • Object包含名为Chars的Property
  • Object包含名为Length的Property
  • 对象包含名为m_stringLength的字段
  • 对象包含名为m_firstChar的字段
  • 对象包含名为Empty的字段
  • 对象包含名为TrimHead的字段
  • 对象包含名为TrimTail的字段
  • 对象包含名为TrimBoth的字段
  • Object包含名为charPtrAlignConst的字段
  • 对象包含名为alignConst的字段

m_firstCharm_stringLength是Properties FirstCharLength的支持字段,但字符串的实际内容保存在Chars属性中。 这是一个索引属性,可以索引它以返回String中的所有字符,但是我找不到包含字符串字符的相应字段。 对此为何的任何想法? 或者如何获取索引属性的支持字段?

属性的支持字段的名称是编译器实现细节,即使您找出模式,也可以在将来进行更改。

我想你已经找到了问题的答案: 忽略所有属性

请记住,一个属性只是一个或两个伪装的function。 在源代码特别请求时,属性将只有编译器生成的支持字段。 例如,在C#中:

 public string Foo { get; set; } 

但是类的创建者不需要像这样使用编译器生成的属性。 例如,属性可能获得常量值,多个属性可能获取/设置位字段的不同部分,依此类推。 在这些情况下,您不希望看到每个属性的单个支持字段。 忽略这些属性是好的。 您的代码不会遗漏任何实际数据。

可以完全忽略所有属性。 如果属性没有支持字段,那么它根本不消耗任何内存。

此外,除非您愿意(尝试)解析CIL,否则您将无法获得此类映射。 考虑以下代码:

 private DateTime today; public DateTime CurrentDay { get { return today; } } 

您如何看待today领域与CurrentDay属性之间存在某种关系?

编辑:关于你最近的问题:

如果您的属性包含return 2.6;等代码return 2.6; ,然后该值不保存在任何地方,该常量直接嵌​​入代码中。

关于stringstring由CLR以特殊方式处理。 如果您尝试反编译其索引器,您会注意到它是由CLR实现的。 对于这些特殊类型( string ,array, int ,…),您无法通过查看其字段来查找它们的大小。 对于所有其他类型,您可以。

回答你的另一个问题:在什么情况下,物业没有支持领域?

 public DateTime CurrentDay { get { return DateTime.Now; } } 

或财产可以使用任何其他数量的支持领域/类

 public string FullName { get {return firstName + " " + lastName;} }