(一)属性的前世今生
程序的本质是“数据+算法”,或者说是用算法来处理数据以期得到输出结果。在程序中,数据表现为各种各样的变量,算法则表现为各种各样的函数(操作符是函数的简记法)。面向对象的类只是把散落在程序中的变更和函数进行归档封装并控制对它们的访问而已。被封装在类里的变更称为字段,它表示类或实例的状态;被封装在类里的函数称为方法,表示类或实例的功能。字段和方法构成了最原始的面向对象封装。
在C#中对类有意义的字段和方法使用static关键字修饰、称为静态成员,通过类名加访问操作符可以访问它们;对类的实例有意义的字段和方法不加static关键字,称为非静态成员或实例成员。
从语义方面来看,静态成员与非静态成员有着很好的对称性,但从程序在内存中的结构来看,这种对称被打破。静态字段在内存中只有一个拷贝,区别只是你能通过类名来访问存放指令的内存还是通过实例名来访问存放指令的内存。
(二) 依赖属性(propdb)
定义:是一种可以自己没有值,并能通过使用Binding从数据源获得值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”。
优点:① 节省实例对内存的开销。② 属性值可以通过Binding依赖在其他对象上。
2.1 依赖属性用法(如下图演示和代码实现)
源码分析前端代码 <StackPanel> <TextBox x:Name="txt1" BorderBrush="Black" Margin="5"/> <TextBox x:Name="txt2" BorderBrush="Black" Margin="5"/> <Button Content="OK" Margin="5" Click="Button_Click"/> </StackPanel> 后台代码 public partial class 属性 : Window { Student stu; public 属性() { InitializeComponent(); stu = new Student(); Binding binding = new Binding("Text") { Source = txt1 }; BindingOperations.SetBinding(stu, Student.NameProperty, binding); txt2.SetBinding(TextBox.TextProperty, binding); } private void Button_Click(object sender, RoutedEventArgs e) { //Stu stu = new Stu(); //stu.SetValue(Stu.NameProperty, this.txt1.Text); //txt2.Text = (string)stu.GetValue(Stu.NameProperty); MessageBox.Show(stu.GetValue(Student.NameProperty).ToString()); } public class Student : DependencyObject { public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student)); public string Name { get { return (string)GetValue(NameProperty); } set { SetValue(NameProperty, value); } } } }
2.2 依赖属性工作原理
使用顺序图
源码分析//DependencyProperty部分源码 public sealed class DependencyProperty { private static Hashtable PropertyFromName = new Hashtable(); //①全局的静态Hashtable private EffectiveValueEntry[] _effectiveValues; //② private static int GlobalIndexCount; //③ private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, System.Windows.ValidateValueCallback validateValueCallback) { FromNameKey key = new FromNameKey(name, ownerType); object synchronized = Synchronized; lock (synchronized) { if (PropertyFromName.Contains(key)) { object[] args = new object[] { name, ownerType.Name }; throw new ArgumentException(MS.Internal.WindowsBase.SR.Get("PropertyAlreadyRegistered", args)); } } if (defaultMetadata == null) { defaultMetadata = AutoGeneratePropertyMetadata(propertyType, validateValueCallback, name, ownerType); } else { if (!defaultMetadata.DefaultValueWasSet()) { defaultMetadata.DefaultValue = AutoGenerateDefaultValue(propertyType); } ValidateMetadataDefaultValue(defaultMetadata, propertyType, name, validateValueCallback); } DependencyProperty dp = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); defaultMetadata.Seal(dp, null); if (defaultMetadata.IsInherited) { dp._packedData |= Flags.IsPotentiallyInherited; } if (defaultMetadata.UsingDefaultValueFactory) { dp._packedData |= Flags.IsPotentiallyUsingDefaultValueFactory; } object obj3 = Synchronized; lock (obj3) { PropertyFromName[key] = dp; } if (TraceDependencyProperty.IsEnabled) { TraceDependencyProperty.TraceActivityItem(TraceDependencyProperty.Register, dp, dp.OwnerType); } return dp; } public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata) { return Register(name, propertyType, ownerType, typeMetadata, null); } public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, System.Windows.ValidateValueCallback validateValueCallback) { RegisterParameterValidation(name, propertyType, ownerType); PropertyMetadata defaultMetadata = null; if ((typeMetadata != null) && typeMetadata.DefaultValueWasSet()) { defaultMetadata = new PropertyMetadata(typeMetadata.DefaultValue); } DependencyProperty property = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback); if (typeMetadata != null) { property.OverrideMetadata(ownerType, typeMetadata); } return property; } public override int GetHashCode() { return this.GlobalIndex; } private class FromNameKey //内部类 { private int _hashCode; private string _name; private Type _ownerType; public FromNameKey(string name, Type ownerType) { this._name = name; this._ownerType = ownerType; this._hashCode = this._name.GetHashCode() ^ this._ownerType.GetHashCode(); } public override bool Equals(object o) { return (((o != null) && (o is DependencyProperty.FromNameKey)) && this.Equals((DependencyProperty.FromNameKey) o)); } public bool Equals(DependencyProperty.FromNameKey key) { return (this._name.Equals(key._name) && (this._ownerType == key._ownerType)); } public override int GetHashCode() { return this._hashCode; } public void UpdateNameKey(Type ownerType) { this._ownerType = ownerType; this._hashCode = this._name.GetHashCode() ^ this._ownerType.GetHashCode(); } } } //DependencyObject类部分源码 public class DependencyObject : DispatcherObject { private EffectiveValueEntry[] _effectiveValues; [MS.Internal.WindowsBase.FriendAccessAllowed] internal static readonly DependencyProperty DirectDependencyProperty = DependencyProperty.Register("__Direct", typeof(object), typeof(DependencyProperty)); internal static System.Windows.DependencyObjectType DType = System.Windows.DependencyObjectType.FromSystemTypeInternal(typeof(DependencyObject)); [MS.Internal.WindowsBase.FriendAccessAllowed] internal void SetValue(DependencyProperty dp, bool value) { this.SetValue(dp, BooleanBoxes.Box(value)); } public void SetValue(DependencyProperty dp, object value) { base.VerifyAccess(); PropertyMetadata metadata = this.SetupPropertyChange(dp); this.SetValueCommon(dp, value, metadata, false, false, OperationType.Unknown, false); } 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; ④ } }
(三)附加属性(propa)
定义:一个属性来来不属于某个对象,但由于某种需求而被后来附加上(类同于装饰模式)。也就是把对象放入一个特定环境后对象才具有的属性(表现出来就是被环境赋予的属性)称之为附加属性(Attached Properties)。
3.1 常见用法
设计理念:作为Button控件的设计者,他不可能预见到控件发布后程序员是把它放在Grid里还是StackPanel(或者未来可以应对的新布局)里,所以他也不可能为Button准备诸如Row,Column等这类属性,那么干脆让布局来决定一个Button用什么属性来设置它的位置。可见,附加属性的作用就是将属性与数据类型(宿主)解耦,上数据类型的设计更加灵活。