flag = value is DeferredReference;
if (!flag && !flag3)
{
throw new ArgumentException(SR.Get(“InvalidPropertyValue”, new object[] { value, dp.Name }));
}
}
}
}
if (operationType == OperationType.ChangeMutableDefaultValue)
{
entry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Default) {
Value = value
};
}
else
{
entry = this.GetValueEntry(entryIndex, dp, metadata, RequestFlags.RawEntry);
}
object localValue = entry.LocalValue;
Expression expression2 = null;
Expression expression3 = null;
if (entry.HasExpressionMarker)
{
if (expr == null)
{
expression3 = _getExpressionCore(this, dp, metadata);
}
if (expression3 != null)
{
localValue = expression3;
expression2 = expression3;
}
else
{
localValue = DependencyProperty.UnsetValue;
}
}
else
{
expression2 = entry.IsExpression ? (localValue as Expression) : null;
}
bool flag5 = false;
if ((expression2 != null) && (expr == null))
{
if (flag)
{
value = ((DeferredReference) value).GetValue(BaseValueSourceInternal.Local);
flag = false;
}
flag5 = expression2.SetValue(this, dp, value);
entryIndex = this.CheckEntryIndex(entryIndex, dp.GlobalIndex);
}
if (flag5)
{
if (entryIndex.Found)
{
entry2 = this._effectiveValues[entryIndex.Index];
}
else
{
entry2 = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp));
}
}
else
{
entry2 = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local);
if ((expression2 != null) && (expression2 != expression3))
{
DependencySource[] sources = expression2.GetSources();
UpdateSourceDependentLists(this, dp, sources, expression2, false);
expression2.OnDetach(this, dp);
entryIndex = this.CheckEntryIndex(entryIndex, dp.GlobalIndex);
}
if (expr == null)
{
entry2.IsDeferredReference = flag;
entry2.Value = value;
entry2.HasExpressionMarker = flag2;
}
else
{
this.SetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, expr, BaseValueSourceInternal.Local);
object defaultValue = metadata.GetDefaultValue(this, dp);
entryIndex = this.CheckEntryIndex(entryIndex, dp.GlobalIndex);
this.SetExpressionValue(entryIndex, defaultValue, expr);
UpdateSourceDependentLists(this, dp, newSources, expr, true);
expr.MarkAttached();
expr.OnAttach(this, dp);
entryIndex = this.CheckEnt
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
ryIndex(entryIndex, dp.GlobalIndex);
entry2 = this.EvaluateExpression(entryIndex, dp, expr, metadata, entry, this._effectiveValues[entryIndex.Index]);
entryIndex = this.CheckEntryIndex(entryIndex, dp.GlobalIndex);
}
}
this.UpdateEffectiveValue(entryIndex, dp, metadata, entry, ref entry2, coerceWithDeferredReference, operationType);
}
}
第一个引起我注意的是DependencySource类,它封装了依赖性对象和依赖性属性,并分别为两个只读属性。
还有一个比较核心的类就是EffectiveValueEntry.
internal EffectiveValueEntry(DependencyProperty dp, BaseValueSourceInternal valueSource)
{
this._propertyIndex = (short) dp.GlobalIndex;
this._value = DependencyProperty.UnsetValue;
this._source = (FullValueSource) valueSource;
}
FullValueSource是一个枚举,如果我没猜错的话,它就是用来标识属性的来源,它有以下几个值:
[FriendAccessAllowed]
internal enum FullValueSource : short
{
HasExpressionMarker = 0x100,
IsAnimated = 0x20,
IsCoerced = 0x40,
IsDeferredReference = 0x80,
IsExpression = 0x10,
ModifiersMask = 0x70,
ValueSourceMask = 15
}
1、通过XAML标记语言来改变属性值,如:
<TextBox x:Name=“txt” Text=“这是一个测试” />
这里就通过XAML标记为 Text属性赋了值。
2、受动画影响而改变的值。比如我画一个矩形,在动画面板中我让它演示长达6秒钟的动画,这期间,矩形的X坐标从20变为80,这个值就是通过动画来设置属性值,这个值是不会提交到属性更改,只是临时更改,动画停止后,属性值将会恢复为原来的值。
3、强制设置值。这个概念很奇怪,可以把它理解为对属性的输入值进行类型转换,我们知道,WPF里面提供了许多XXXXConvertor,毕竟我们在XAML标记中只能输入文本值,比如颜色,我们都会输入Red这样的值,其实在后台,运行库把字符串的值转换为画刷实例,再向属性赋值。
4、被改变后的值,从跟踪的结果看,依赖项属性的值都有N个版本,分别为不同的变量来保存,以便做验证和对比。
EffectiveValueEntry里面就是保存依赖项属性的全局索引和属性值,继续反编译,发现了一个InsertEntry方法,比较长,代码我不帖,只帖签名。
private void InsertEntry(EffectiveValueEntry entry, uint entryIndex)
两个参数都很好理解,第二个参数就是数组的索引,那么,是哪个数组的索引呢?接着反编译,看到DendencyObject类有一个内部字段,定义如下:
private EffectiveValueEntry[] _effectiveValues;
对,这下找到了,就是一个EffectiveValueEntry的数组,好了,不用往下跟了,到这里基本上可以看出依赖项属性是如何保存它的值了,原理如下:
每个DendencyObject类的实例都会创建一个专门的数组,数组中的每个元素分别标识着该类型的一个依赖项属性,例如我定义了一个类A,A里面定义了3个依赖项属性A.kk,A.cc,A.ff,这样当A类被分配到内存中实例化的时候,创建一个EffectvieValueEntry数组,而每个EffectiveValueEntry对应着一个属性,保存着该属性的不同版本的值,A类型有3个依赖项属性,所以对应的EffectiveEntry数组就有3个元素。
那么,CLR如何知道哪个EffectiveEntry对应着哪个依赖项属性呢?前面说过,每个依赖项属性都会以哈希值的键保存到一个全局哈希表中,所以,只要查找出对应键就可以唯一地标识依赖项属性了。
既然知道了如何设置值,那么,对于如何获取值就更好办了,过程刚好相反。
public object GetValue(DependencyProperty dp)
{
base.VerifyAccess();
if (dp == null)
{
throw new ArgumentNullException(“dp”);
}
return this.GetValueEntry(this.LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value;
}
看到最后一行,其实返回了一个EffectiveValueEntry类型的变量,再从中取出Value属性的值,这个值其实就是对应类型的依赖项属性的值。
那么,EffectiveValueEntry数组中的元素是在哪儿赋值的呢?我翻遍了整个DependencyObject类也没有找到赋值的语句,这时候我突然想起刚才的SetValue方法,注意到这几行:
if (operationType == OperationType.ChangeMutableDefaultValue)
{
entry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Default) {
Value = value
};
}
else
{
entry = this.GetValueEntry(entryIndex, dp, metadata, RequestFlags.RawEntry);
}
如果要设置的值的等于默认,还记得我们调用Register注册依赖项属性时,最后一个参数,它是一个PropertyMetaData类型,它封装了依赖项属性的默认值,也就是元数据,元数据说白了就初始值,“元”在汉语中有“首”,“开始”,“第一”等意思,如“XX元年”、“公元2002年”等。
所以,上面的代码是:如果设置的值与元数据一致,就不用赋值了,直接从元数据里面,这样可以免去了在赋值过程发生的“拆箱”和“装箱”行为消耗性能;
如果设置的值不是元数据(默认值),那么就动态创建一个EffectvieValueEntry类的实例,并保存当前值。
因此,EffectiveValueEntry数组是动态分配内存的,就是说,如果你不对属性进行赋值,那就不创建EffectiveValueEntry实例,因为依赖项属性在静态注册时就带了默认值。
因此,对比传统的面向对象属性系统,WPF的依赖项属性有以下优点:
1、类被实例化时不对属性进行初始化,大大节省了内存空间。