元数据类包含哪些成员以及有几个构造函数重载因为这些直接关系到外部的调用。2大家要注意ValidateValueCallback不是PropertyMetadata的成员所以在PropertyMetadata的构造函数中不要把它作为参数传入。3注意OnApply函数因为调用它之后就不能修改元数据的成员只有通过OverrideMetadata和AddOwner间接实现如果大家想知道到底这个元数据有没有被密封可以调用IsSealed属性来查看这个功能我们也会经常用到。4元数据类中提供了Merge的功能用来方便合并父类和子类的元数据。1: namespace System.Windows2: {3: //依赖属性三大回调委托PropertyChangedCallback、CoerceValueCallback和ValidateValueCallback4: public delegate void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e);5: public delegate object CoerceValueCallback(DependencyObject d, object baseValue);6: public delegate bool ValidateValueCallback(object value);7:8: public class PropertyMetadata9: {10: private object defaultValue;11: private bool isSealed;12: private PropertyChangedCallback propertyChangedCallback;13: private CoerceValueCallback coerceValueCallback;14:15: //返回该元数据是否已密封16: protected bool IsSealed17: {18: get { return isSealed; }19: }20:21: //获取和设置元数据默认值22: public object DefaultValue23: {24: get { return defaultValue; }25: set26: {27: if (IsSealed)28: throw new InvalidOperationException(Cannot change metadata once it has been applied to a property);29: if (value DependencyProperty.UnsetValue)30: throw new ArgumentException(Cannot set property metadatas default value to Unset);31:32: defaultValue value;33: }34: }35:36: //ChangedCallback委托赋值注意检查元数据是否已经密封37: public PropertyChangedCallback PropertyChangedCallback38: {39: get { return propertyChangedCallback; }40: set41: {42: if (IsSealed)43: throw new InvalidOperationException(Cannot change metadata once it has been applied to a property);44: propertyChangedCallback value;45: }46: }47:48: //CoerceValueCallback委托赋值注意检查元数据是否已经密封49: public CoerceValueCallback CoerceValueCallback50: {51: get { return coerceValueCallback; }52: set53: {54: if (IsSealed)55: throw new InvalidOperationException(Cannot change metadata once it has been applied to a property);56: coerceValueCallback value;57: }58: }59:60: #region PropertyMetadata构造函数根据不同参数做初始化操作61: public PropertyMetadata()62: : this(null, null, null)63: {64: }65:66: public PropertyMetadata(object defaultValue)67: : this(defaultValue, null, null)68: {69: }70:71: public PropertyMetadata(PropertyChangedCallback propertyChangedCallback)72: : this(null, propertyChangedCallback, null)73: {74: }75:76: public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback)77: : this(defaultValue, propertyChangedCallback, null)78: {79: }80:81: public PropertyMetadata(object defaultValue, PropertyChangedCallback propertyChangedCallback, CoerceValueCallback coerceValueCallback)82: {83: if (defaultValue DependencyProperty.UnsetValue)84: throw new ArgumentException(Cannot initialize property metadatas default value to Unset);85:86: this.defaultValue defaultValue;87: this.propertyChangedCallback propertyChangedCallback;88: this.coerceValueCallback coerceValueCallback;89: }90: #endregion91:92: //合并元数据93: protected virtual void Merge(PropertyMetadata baseMetadata, DependencyProperty dp)94: {95: if (defaultValue null)96: defaultValue baseMetadata.defaultValue;97: if (propertyChangedCallback null)98: propertyChangedCallback baseMetadata.propertyChangedCallback;99: if (coerceValueCallback null)100: coerceValueCallback baseMetadata.coerceValueCallback;101: }102:103: protected virtual void OnApply(DependencyProperty dp, Type targetType)104: {105: //留给子类来实现吧106: }107:108: //合并元数据并密封109: internal void DoMerge(PropertyMetadata baseMetadata, DependencyProperty dp, Type targetType)110: {111: Merge(baseMetadata, dp);112: OnApply(dp, targetType);113: isSealed true;114: }115: }116: }在上面几个类就是依赖属性系统的核心类下面将看到几个Helper类。十二. 其他协助类测试代码这里就简单写一下对DependencyObjectTypeTest的测试代码1: using System;2: using System.Windows;3: using NUnit.Framework;4:5: namespace TDDDependencyTest.System.Windows6: {7: [TestFixture]8: public class DependencyObjectTypeTest9: {10:11: [Test]12: public void Accessors()13: {14: DependencyObjectType t DependencyObjectType.FromSystemType(typeof(TestDepObj));15: Assert.AreEqual(TestDepObj, t.Name);16: Assert.AreEqual(typeof(TestDepObj), t.SystemType);17: Assert.AreEqual(typeof(DependencyObject), t.BaseType.SystemType);18: }19:20: [Test]21: public void IsInstanceOfType()22: {23: DependencyObjectType t DependencyObjectType.FromSystemType(typeof(TestDepObj));24: DependencyObjectType t2 DependencyObjectType.FromSystemType(typeof(TestSubclass));25: Assert.IsTrue(t.IsInstanceOfType(new TestSubclass()));26: Assert.IsTrue(t2.IsSubclassOf(t));27: Assert.IsFalse(t.IsSubclassOf(t2));28: }29:30: [Test]31: public void TestCache()32: {33: DependencyObjectType t DependencyObjectType.FromSystemType(typeof(TestDepObj));34: DependencyObjectType t2 DependencyObjectType.FromSystemType(typeof(TestDepObj));35: Assert.AreSame(t, t2);36: }37: }38: }由于它的功能比较简单所以我们就不做过多介绍大家想了解更多可以参看代码。十三. 其他协助类的实现代码LocalValueEnumerator手动实现一个IEnumerator来方便访问LocalValue1: using System;2: using System.Collections.Generic;3: using System.Linq;4: using System.Text;5: using System.Collections;6:7: namespace System.Windows8: {9: //手动实现一个IEnumerator来方便访问LocalValue10: public struct LocalValueEnumerator : IEnumerator11: {12: private IDictionaryEnumerator propertyEnumerator;13: private DictionaryDependencyProperty, object properties;14:15: private int count;16:17: internal LocalValueEnumerator(DictionaryDependencyProperty, object properties)18: {19: this.count properties.Count;20: this.properties properties;21: this.propertyEnumerator properties.GetEnumerator();22: }23:24: public int Count25: {26: get { return count; }27: }28:29: //获取当前LocalValue30: public LocalValueEntry Current31: {32: get33: {34: return new LocalValueEntry((DependencyProperty)propertyEnumerator.Key,35: propertyEnumerator.Value);36: }37: }38:39: object IEnumerator.Current40: {41: get { return this.Current; }42: }43:44:45: public bool MoveNext()46: {47: return propertyEnumerator.MoveNext();48: }49:50: //重置propertyEnumerator51: public void Reset()52: {53: propertyEnumerator.Reset();54: }55:56: public static bool operator !(LocalValueEnumerator obj1, LocalValueEnumerator obj2)57: {58: throw new NotImplementedException();59: }60:61: public static bool operator (LocalValueEnumerator obj1, LocalValueEnumerator obj2)62: {63: throw new NotImplementedException();64: }65:66: public override bool Equals(object obj)67: {68: throw new NotImplementedException();69: }70:71: public override int GetHashCode()72: {73: throw new NotImplementedException();74: }75: }76:77: //LocalValue实体类78: public struct LocalValueEntry79: {80: private DependencyProperty property;81: private object value;82:83: internal LocalValueEntry(DependencyProperty property, object value)84: {85: this.property property;86: this.value value;87: }88:89: public DependencyProperty Property90: {91: get { return property; }92: }93:94: public object Value95: {96: get { return value; }97: }98:99: public static bool operator !(LocalValueEntry obj1, LocalValueEntry obj2)100: {101: throw new NotImplementedException();102: }103:104: public static bool operator (LocalValueEntry obj1, LocalValueEntry obj2)105: {106: throw new NotImplementedException();107: }108:109: public override bool Equals(object obj)110: {111: throw new NotImplementedException();112: }113:114: public override int GetHashCode()115: {116: throw new NotImplementedException();117: }118: }119: }120:DependencyPropertyChangedEventArgsPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)的参数它的第一个参数为该DependencyProperty、第二个参数为原来的值、第三个参数为改变了的值。1: using System;2: using System.Collections.Generic;3: using System.Linq;4: using System.Text;5:6: namespace System.Windows7: {8: public class DependencyPropertyChangedEventArgs9: {10: //第一个参数为该DependencyProperty、第二个参数为原来的值、第三个参数为新值11: public DependencyPropertyChangedEventArgs(DependencyProperty property, object oldValue, object newValue)12: {13: this.Property property;14: this.OldValue oldValue;15: this.NewValue newValue;16: }17:18: //注意所有的属性只对外界开放只读操作19: public object NewValue20: {21: get;22: private set;23: }24:25: public object OldValue26: {27: get;28: private set;29: }30:31: public DependencyProperty Property32: {33: get;34: private set;35: }36:37: public override bool Equals(object obj)38: {39: if (!(obj is DependencyPropertyChangedEventArgs))40: return false;41:42: return Equals((DependencyPropertyChangedEventArgs)obj);43: }44:45: public bool Equals(DependencyPropertyChangedEventArgs args)46: {47: return (Property args.Property 48: NewValue args.NewValue 49: OldValue args.OldValue);50: }51:52: public static bool operator !(DependencyPropertyChangedEventArgs left, DependencyPropertyChangedEventArgs right)53: {54: throw new NotImplementedException();55: }56:57: public static bool operator (DependencyPropertyChangedEventArgs left, DependencyPropertyChangedEventArgs right)58: {59: throw new NotImplementedException();60: }61:62: public override int GetHashCode()63: {64: throw new NotImplementedException();65: }66:67: }68: }DependencyPropertyKey构造函数传入该DependencyProperty然后通过Type来OverrideMetadata此类只是起到了封装作用。1:2: namespace System.Windows3: {4: //构造函数传入该DependencyProperty然后通过Type来OverrideMetadata5: public sealed class DependencyPropertyKey6: {7: internal DependencyPropertyKey (DependencyProperty dependencyProperty)8: {9: this.dependencyProperty dependencyProperty;10: }11:12: private DependencyProperty dependencyProperty;13: public DependencyProperty DependencyProperty {14: get { return dependencyProperty; }15: }16:17: public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata)18: {19: dependencyProperty.OverrideMetadata (forType, typeMetadata, this);20: }21: }22: }DependencyObjectType用静态DictionaryType, DependencyObjectType来存储DependencyObjectType主要有FromSystemType、IsInstanceOfType和IsSubclassOf三个功能。1: using System;2: using System.Collections.Generic;3: using System.Linq;4: using System.Text;5:6: namespace System.Windows7: {8: public class DependencyObjectType9: {10: //键为Type即OwnerType值为DependencyObjectType即ID和systemType的键值对11: private static DictionaryType, DependencyObjectType typeMap new DictionaryType, DependencyObjectType();12: private static int current_id;13:14: private int id;15: private Type systemType;16:17: //构造函数私有在FromSystemType里进行构造初始化id和systemType18: private DependencyObjectType(int id, Type systemType)19: {20: this.id id;21: this.systemType systemType;22: }23:24: //基类型的DependencyObjectType25: public DependencyObjectType BaseType26: {27: get { return DependencyObjectType.FromSystemType(systemType.BaseType); }28: }29:30: public int Id31: {32: get { return id; }33: }34:35: public string Name36: {37: get { return systemType.Name; }38: }39:40: public Type SystemType41: {42: get { return systemType; }43: }44:45: //用静态DictionaryType, DependencyObjectType来存储DependencyObjectType46: public static DependencyObjectType FromSystemType(Type systemType)47: {48: if (typeMap.ContainsKey(systemType))49: return typeMap[systemType];50:51: DependencyObjectType dot;52:53: typeMap[systemType] dot new DependencyObjectType(current_id, systemType);54:55: return dot;56: }57:58: //是否是该DependencyObject的子类实例59: public bool IsInstanceOfType(DependencyObject d)60: {61: return systemType.IsInstanceOfType(d);62: }63:64: //该DependencyObjectType是否是传入DependencyObjectType的子实例65: public bool IsSubclassOf(DependencyObjectType dependencyObjectType)66: {67: return systemType.IsSubclassOf(dependencyObjectType.SystemType);68: }69:70: public override int GetHashCode()71: {72: throw new NotImplementedException();73: }74: }75: }76:十四. 回归并统计覆盖率在上面的开发过程中我们会不断的运行和查看代码通过情况最后我们也来看一下测试用例的总体通过情况其实在前面已经运行过很多次了因为每个功能都要经过”测试代码功能代码测试重构“等步骤。最后也看一下代码测试覆盖率代码测试覆盖率对一个系统或者产品来说是一个比较重要的质量指标可以通过它看出系统的稳定性和可控性。一般在项目的开发中我们都会以85%~90%的测试代码覆盖率作为达标的参考标准。由于MONO本身对依赖属性没有那么健全我们也没有写那么详细的测试代码中间直接就实现了一些功能严格地说所以本文并没有完全遵从正规的测试驱动开发流程。十五. 简单验证依赖属性系统其实通过上面的测试用例基本就用不着再单独测试了但鉴于覆盖率比较低的问题所以最后我们还是来测试一下刚才构建的依赖属性系统1: class Program2: {3: static void Main(string[] args)4: {5: SimpleDPClass sDPClass new SimpleDPClass();6: sDPClass.SimpleDP 8;7: Console.ReadLine();8: }9: }10:11: public class SimpleDPClass : DependencyObject12: {13: public static readonly DependencyProperty SimpleDPProperty 14: DependencyProperty.Register(SimpleDP, typeof(double), typeof(SimpleDPClass),15: new PropertyMetadata((double)0.0,16:17: new PropertyChangedCallback(OnValueChanged),18: new CoerceValueCallback(CoerceValue)),19: new ValidateValueCallback(IsValidValue));20:21: public double SimpleDP22: {23: get { return (double)GetValue(SimpleDPProperty); }24: set { SetValue(SimpleDPProperty, value); }25: }26:27: private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)28: {29: Console.WriteLine(当值改变时我们可以做的一些操作具体可以在这里定义 {0}, e.NewValue);30: }31:32: private static object CoerceValue(DependencyObject d, object value)33: {34: Console.WriteLine(对值进行限定强制值 {0}, value);35: return value;36: }37:38: private static bool IsValidValue(object value)39: {40: Console.WriteLine(验证值是否通过如果返回True表示验证通过否则会以异常的形式暴露 {0}, value);41: return true;42: }43:44: }测试结果到处为止我们这篇文章也宣告结束。十六. 本文总结本篇承接上一篇WPF基础到企业应用系列7——深入剖析依赖属性的写作风格对上篇模拟一个WPF依赖属性的实现重现演绎了一遍上篇是根据微软WPF的BCL源码剖析的所以这篇我们就详细的研究一下.NET的跨平台版本MONO关于依赖属性系统的实现。在这篇文章中我只是起到了剖析源码的作用就像研究微软的BCL一样不过MONO的代码远没有微软的BCL那么庞大所以研究和复原起来不是很吃力。如果大家还想继续深入可以去下载相关源码也希望大家和我一起交流探讨。十七. 相关代码下载在文章的最后和往常一样我们提供代码的下载再次温馨提示这几篇文章最重要的就是下载代码来细细研究代码里面也添加了比较详细的注释如果大家有什么问题也可以直接和我联系如果有不正确的地方也希望多多海涵并能给我及时反馈我将感激不尽