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?
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.
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.
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