【问题标题】:Using Reflection to determine which Fields are backing fields of a Property使用反射来确定哪些字段是属性的支持字段
【发布时间】:2012-12-28 15:52:54
【问题描述】:

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

例如:

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 的引擎盖时,您会看到以下内容:

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

m_firstCharm_stringLength 是属性 FirstCharLength 的支持字段,但字符串的实际内容保存在 Chars 属性中。这是一个索引属性,可以对其进行索引以返回字符串中的所有字符,但我找不到保存字符串字符的相应字段。关于为什么会这样的任何想法?或者如何获取索引属性的支持字段?

【问题讨论】:

  • 如果两个属性共享相同的支持字段,您想做什么?或者如果一个只读属性没有?还是该属性返回某种类型的类对象?
  • 在任何一种情况下,结构的内存大小不只是字段的大小吗?那么您难道不只关心没有支持字段的属性吗?那些看起来像什么?
  • @Bobson- 通过使用 FieldInfo 和 PropertyInfo GetValue() 方法,我的代码中涵盖了任何引用类型,然后递归地查看它们的字段和属性。当递归链将一个对象一直映射到它的值类型时,它基本上停止了。如果两个属性共享相同的支持字段,我只想评估该支持字段的大小一次。你让我想到了另一个我想问的问题,但我很难找到答案。什么情况下属性没有后备字段?我将编辑问题以包含此内容。
  • @Chris Damour- 这是对我的问题的一个很好的改进。我相信我只关心没有支持字段的属性。很难说出它们的样子,因为我在网上找不到示例。我遇到过它,但在我自己的托管代码中重新创建它时遇到了麻烦。
  • @user1106760 这些属性是否在框架代码中?如果是这样,您能告诉我们您具体指的是哪些属性吗?

标签: c# reflection properties field memory-consumption


【解决方案1】:

属性的支持字段的名称是编译器实现的细节,并且在将来总是可以改变,即使你弄清楚了模式。

我认为您已经找到了问题的答案:忽略所有属性

请记住,属性只是变相的一两个函数。只有在源代码特别请求时,属性才会有编译器生成的支持字段。例如,在 C# 中:

public string Foo { get; set; }

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

【讨论】:

  • 感谢您的回复。回应:“一个属性只有在源代码特别要求时才会有编译器生成的支持字段。”这些编译器生成的字段是否总是列在 Type.GetFields() 集合中?
  • 是的,但我的回答可能不是很清楚。关键是属性是方法而不是数据。一个属性可能引用一个字段、多个字段或根本没有字段。如果您对对象的内存布局感兴趣,则无需考虑它们的属性。
【解决方案2】:

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

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

private DateTime today;

public DateTime CurrentDay
{
    get { return today; }
}

您希望如何确定today 字段和CurrentDay 属性之间存在某种关系?

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

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

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

【讨论】:

  • 感谢您的回复。这几乎正​​是我试图问的问题。我不确定属性与其对应的支持字段之间是否存在某种明确的关系,这些关系可以通过我尚未找到的一些反射函数获得。糟透了,似乎没有。
  • 我不确定我是否完全相信 Type.GetFields() 集合中没有相应支持字段的属性不会消耗任何内存。我觉得有一个例子,支持字段不会被列出,因为它没有明确声明......你有没有我可以阅读的声明的来源?再次感谢您。
  • @user1106760 正如我(和其他人)试图解释的那样:你不能这样做,因为属性和字段之间根本没有任何“明确的关系”。属性可以包含任何代码,因此对于它们中的许多,“支持字段”的概念没有任何意义。
  • @user1106760 在 CIL 级别(几乎是反射操作的地方),属性只是几个方法,仅此而已。因此,属性本身不可能消耗任何内存。而且没有办法在 CIL 或类似的东西中“隐式”声明一个字段。
  • @svick- 好的,我想我现在明白了。感谢您的帮助。
【解决方案3】:

回答您的另一个问题:属性在什么情况下没有支持字段?

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

或属性可以使用任何其他数量的支持字段/类

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

【讨论】:

    猜你喜欢
    • 2017-04-10
    • 2013-05-12
    • 2013-05-06
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 2021-09-23
    • 2014-10-09
    相关资源
    最近更新 更多