Using Reflection to determine which Fields are backing fields of a Property

家住魔仙堡 提交于 2019-11-30 08:28:25

问题


I'm using reflection to map out objects. These objects are in managed code but I have no visibility into their source code, underlying structure, etc. other than through reflection. The overarching goal of all this is a rudimentary memory map of an object (similar in functionality to SOS.dll DumpObject and !ObjSize commands). As such, I'm trying to determine which members are being "double counted" as both a field and a property.

For example:

public class CalendarEntry
{
    // private property 
    private DateTime date { get; set;}

    // public field 
    public string day = "DAY";
}

When mapped shows:

  • Fields
    • day
    • k__BackingField
  • Properties
    • date

Where as a class like this:

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

When mapped shows:

  • Fields
    • day
    • date
  • Properties
    • Date

At first glance there's nothing to tell you that the Date property's "backing field" is the field named date. I'm trying to avoid counting date twice in this scenario since that will give me a bad memory size approximation.

What's more confusing/complicated is I've come across scenarios where properties don't always have a corresponding field that will be listed through the Type.GetFields() method so I can't just ignore all properties completely.

Any ideas on how to determine if a field in the collection returned from Type.GetFields() is essentially the backing field of some corresponding property returned from Type.GetProperties()?

Edit- I've had trouble determining what conditions a property will not have a corresponding field in listed in the collection returned from Type.GetFields(). Is anyone familiar with such conditions?

Edit 2- I found a good example of when a property's backing field would not be included in the collection returned from Type.GetFields(). When looking under the hood of a String you have the following:

  • Object contains Property named FirstChar
  • Object contains Property named Chars
  • Object contains Property named Length
  • Object contains Field named m_stringLength
  • Object contains Field named m_firstChar
  • Object contains Field named Empty
  • Object contains Field named TrimHead
  • Object contains Field named TrimTail
  • Object contains Field named TrimBoth
  • Object contains Field named charPtrAlignConst
  • Object contains Field named alignConst

The m_firstChar and m_stringLength are the backing fields of the Properties FirstChar and Length but the actual contents of the string are held in the Chars property. This is an indexed property that can be indexed to return all the chars in the String but I can't find a corresponding field that holds the characters of a string. Any thoughts on why that is? Or how to get the backing field of the indexed property?


回答1:


The name of a property's backing field is a compiler implementation detail and can always change in the future, even if you figure out the pattern.

I think you've already hit on the answer to your question: ignore all properties.

Remember that a property is just one or two functions in disguise. A property will only have a compiler generated backing field when specifically requested by the source code. For example, in C#:

public string Foo { get; set; }

But the creator of a class need not use compiler generated properties like this. For example, a property might get a constant value, multiple properties might get/set different portions of a bit field, and so on. In these cases, you wouldn't expect to see a single backing field for each property. It's fine to ignore these properties. Your code won't miss any actual data.




回答2:


You can ignore all properties completely. If a property doesn't have a backing field, then it simply doesn't consume any memory.

Also, unless you're willing to (try to) parse CIL, you won't be able to get such mapping. Consider this code:

private DateTime today;

public DateTime CurrentDay
{
    get { return today; }
}

How do you expect to figure out that there is some relation between the today field and the CurrentDay property?

EDIT: Regarding your more recent questions:

If you have property that contains code like return 2.6;, then the value is not held anywhere, that constant is embedded directly in the code.

Regarding string: string is handled by CLR in a special way. If you try to decompile its indexer, you'll notice that it's implemented by the CLR. For these few special types (string, array, int, …), you can't find their size by looking at their fields. For all other types, you can.




回答3:


To answer your other question:Under what circumstances do properties not have backing fields?

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

or property may use any other number of backing fields/classes

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


来源:https://stackoverflow.com/questions/14322660/using-reflection-to-determine-which-fields-are-backing-fields-of-a-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!