Abbreviations used:
DP=DependencyProperty
dp#=DP instances, e.g, DP1
DO=DependencyObject
dp#=DO instances
Good good question, but I don't think the accepted answer and comments really got what you're asking, what a pity. A more clarified question might be:
How a DependencyObject instance stores its local DependencyProperty(s) and retrieves it(them) by GetValue
and SetValue
?
According to your description, you're on the write way to explore out the way, what you should do is to track down the source code a little bit more:
DependencyObject.SetValue
DependencyObject.GetValue
Before digging into details, bear in mind that one of the advantages the dependency mechanism offers is helping to Reduced memory footprint. So you can roughly imagine how it works: at the very beginning, each DO instance has zero local DPs; and once it set a local DP by invoking the according SetValue
, it allocates some space in memory to store it. Although the description is far from the truth, at least it gives you a preliminary grasp.
DependencyProperty
First, let's focus on the DP. As we know, each DP instance will be registered only once, and DP has a private static field int GlobalIndexCount
, which increments by 1 each time a new DP instance is registered. Accordingly, each DP instance has a GlobalIndex
, a possible example might be as:
DP instances |
GlobalIndex |
dp1 |
0 |
dp2 |
1 |
dp3 |
2 |
Current static field GlobalIndexCount for DP |
3 (which will be assigned to next registered DP instance) |
Btw, all DP instances are maintained by Hashtable PropertyFromName
which is also a static field of class DependencyProperty
. Bear in mind that each DP class instantiates only once.
DependencyObject
Second, let's have a look at DO. Each DO instance maintains an array: private EffectiveValueEntry[] _effectiveValues;
which stores all the local DP instances it needs. EffectiveValueEntry
has two properties:
PropertyIndex
: exactly GlobalIndex of a specific DP instance
Value
: the local value of the DP setted by SetValue
Suppose we have a DO class defined as below:
// Keep declaration only and omit trivial code
public class MyDependencyObject : DependencyObject
{
protected static DependencyProperty Test1Property;
protected static DependencyProperty Test2Property;
protected static DependencyProperty Test3Property;
}
MyDependencyObject do1...;
do1.Test1 = 1;
do1.Test3 = 3;
MyDependencyObject do2...;
do2.Test2 = 2;
Now _effectiveValues
in do1
looks like this
EntryIndex.Index |
PropertyIndex (equiv. GlobalIndex for DP), sorted |
Value |
Description |
0 |
0 |
1 |
Local value for Test1Property |
1 |
2 |
3 |
Local value for Test3Property |
where EntryIndex
is returned by the function LookupEntry
you mentioned. For situation in do2:
Entry index |
PropertyIndex (equiv. GlobalIndex for DP), sorted |
Value |
0 |
1 |
Local value for Test2Property |
All the EffectiveValueEntry
stored in _effectiveValues
are sorted by GlobalIndex
The keypoint
Now it comes to the core of you puzzle: when a DO instance tries to get a DP, it use the DP's GlobalIndex
to search its private field _effectiveValues
, if found, a local DP value is returned; else, a default or inherited value is returned. Actually, the true story is much more complicated, see Value resolution strategy.
When it comes to write instead of read, similar stories happen, only by not reading but modifying _effectiveValues
.