1. 属性的来龙去脉

被封装在类里的变量称为字段,它表示的是类或实例的状态;被封装在类里的函数叫做方法,它表示的是类或实例的功能。字段和类构造出了最原始的面向对象封装,这时候的面向对象中还不包含事件,属性等概念。

静态字段在内存中只有一个拷贝,非静态字段则每个实例都有一个拷贝,无论方法是静态还是非静态,在内存中都只有一个拷贝,区别只是你能通过类名来访问存放内存中的指令还是通过实例来访问。

字段被封装在实例里,要么能被外接访问, 要么不能,这种直接把数据暴露给外界的做法很不安全,很容易就把错误的值写入字段。如果在每次写入字段的时候先判断一下值的有效性又会增加冗余的代码并违反了面向对象要求的“高内聚”的原则。

于是,当Framwork推出以后,微软进一步对Get/Set这对方法进行了封装合并成了属性(Property)。使用属性的时候,格式上很像使用非private字段,保证了语义上的顺畅,同时又不失Get/Set方法的安全性,代码也变的更加紧凑。

public class PersonTest{private int age;//下面为方法public int GetAge(){ return this.age;}public void SetAge(int value){this.age = value;}//下面为属性public int Age{get { return this.Age;}set { this.Age = value; }}}

那么是不是每个对象的CLR属性也会多占一点内存呢?想得到这个答案,使用IL反编译器打开编译结果:

通过IL工具我们看到:属性也被编译新的方法。说明属性就是方法的另外一种写法。对于一个非静态类的字段每实例化一个对象,内存就准备一些空间,用来存储字段的值。这样的话,肯定会带来资源的浪费,因为有的字段是不怎么常用,但是实例化的话还是要照类的标准去实例化,每个字段都要按一定的方式得到其值。为了解决这个问题,我们就引出依赖属性。

2. 依赖属性

依赖属性就是可以自己没有值,并能够通过Binding从数据源获取值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”。与传统的CLR属性和面向对象相比依赖属性有很多新颖之处,其中包括:
节省实例对内存的开销。
属性值可以通过Binding依赖在其它对象上。

让我们去思考一个现实世界中存在的问题:一个登山队员,他的全套装备有很多,包括登山服、登山靴、登山仗、护目镜、绳索、无线电、水、食品甚至还有氧气瓶等。倘若是去等珠穆朗玛峰,这些装备都要带上,要是去登香山呢?如果也背着氧气瓶岂不怪哉!所以,实际的一点办法就是---用得着的就带上,用不着的就不带,有必要的时候可以借别人的用一下。

其实,这就是WPF中依赖属性的原理。传统的.NET开发中,一个对象所占用的内存空间在调用new操作符进行实例化的时候就已经决定了,而WPF允许对象在被创建的时候并不包含用于存储数据的空间(即字段所占用的空间)、只保留在需要用到数据的时候能够获得默认值、借用其它对象的数据或者实时分配空间的能力----这种对象称为依赖对象而他这种实时获取数据的能力则依靠依赖属性来实现。在WPF开发中,必须使用依赖对象作为依赖属性的宿主,使二者结合起来,才能形成完整的Binding目标被数据所驱动。

在WPF系统中,依赖对象的概念被DependencyObject类所实现。依赖属性的概念则由DependencyProperty来实现。DependencyObject具有GetValue和SetValue两个方法。WPF的所有控件都是依赖对象。WPF的类库在设计的时候充分利用了依赖属性的优势,UI控件的绝大多数属性已经依赖化了。

DependencyProperty必须以DependencyObject作为宿主,借助它的SetValue和GetValue进行记录和读取。因此,想使用自定义的DependencyProperty,宿主一定是DependencyObject的派生类。DenpendencyProperty实例声明特点很鲜明----引用变量由public static readonly三个修饰符进行修饰,实例并非使用new操作符得到而是使用DependencyProperty.Register方法实现。使用一个命名约定----成员变量的名字需要加入Property的后缀表明它是一个依赖属性。我们打算用这个依赖属性存学生的姓名,所以把它命名为NameProperty。

使用DependencyProperty.Register方法创建,分析一下这三个参数:

第一个参数为string类型,用这个参数指明哪个CLR属性作为这个依赖属性的包装器,或者说此依赖属性支持的是哪个CLR属性。包装器的作用是以“实例属性”的形式向外界暴露依赖属性,这样一个依赖属性才能成为数据源的path

第二个参数用来指明此依赖属性用来存储什么类型的值,学员的姓名是string类型的值,所以这个参数被赋值为typeof(string)。
    第三个参数用来指明此依赖属性的宿主是什么类型,或者说DependencyProperty.Register方法将这个依赖属性关联到哪个类型上。本例的意图是为Student类准备一个可依赖的名称属性,所以需要将NameProperty和Student进行关联。因此该参数被赋值为typeof(Student)。

实例:我们使用Binding将Student关联到TextBox1上,在把TextBox2的值关联到Student对象上形成Binding链。

XAML:

<Window x:Class="第七章属性.wnd72"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="120" Width="525"><StackPanel Background="LightSlateGray"><TextBox x:Name="_txtBox1" Height="28" Margin="5"/><TextBox x:Name="_txtBox2" Height="28" Margin="5"/></StackPanel>
</Window>

C#:

    /// <summary>/// 声明类为依赖对象 -- 依赖属性必须基于依赖对象宿主/// </summary>public class Student:DependencyObject{/// <summary>/// CLR属性读写的依赖属性的值/// 输入"propdp", 生成依赖属性模版, 通过"Tab"键调节名称即可/// </summary>public string Name{get { return (string)GetValue(NameProperty); }set { SetValue(NameProperty, value); }}/// <summary>/// NameProperty为命名规范,与下面括号里的第一个参数(我们以前用的CLR属性)一致,然后加上Property。/// 第二个参数是“字段”的类型/// 第三个为依赖属性的宿主(目标Target)的类型/// </summary>public static readonly DependencyProperty NameProperty =DependencyProperty.Register("Name", typeof(string), typeof(Student));/// <summary>/// 为了方便操作/// </summary>public BindingExpressionBase SetBinding(DependencyProperty dp, BindingBase binding){return(BindingOperations.SetBinding(this, dp, binding)); }}/// <summary>/// wnd72.xaml 的交互逻辑/// </summary>public partial class wnd72 : Window{Student stu = new Student();public wnd72(){InitializeComponent();stu.SetBinding(Student.NameProperty, new Binding("Text") { Source = _txtBox1 });_txtBox2.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = stu });}}

运行程序的时候,当TextBox1的时候中输入字符的时候,TextBox2也会同步显示。当然,此时的Student对象的Name属性值也同步发生变化了。

在一个类中声明依赖属性并不需要手动进行声明、注册并使用CLR属性进行封装,只需要输入propdp,VisualStudio提示列表就会有一项高亮显示,连续按两次Tab键,一个标准的依赖属性(带CLR属性包装)就声明好了,再按动Tab键,可以在提示环境中修改依赖属性的各个参数。这个功能称为snippet(称为代码模板或代码片段),这是VisulaStudio中所有非简化版本自带的功能,多多掌握这个功能可以大大的提高编码速率和降低错误率。

值得注意的是,尽管Student类没有实现INotifyPropertyChanged接口,当属性的值发送改变时与之关联的binding对象依然可以得到通知,依赖属性默认的带有这种功能,天生就是合格的数据源

1.3 附加属性

附加属性就是说一个属性本来不属于某个对象,但由于某种需求后来又被附加上了。也就是说把对象放入一个特定的环境对象才能拥有该属性,这种属性就是附加属性。实际开发工作中,我们经常会遇到这种情况,比如一个名为Human的类,它有可能被与学校相关的工作流用到(记录它的班级,年级等信息),也有可能被与学校相关的工作流用到(记录他的部门,项目)。

那么设计类的时候我们是不是要这样做呢?显然占用更多内存、维护很麻烦。

public class Human  {  public int Id { get; set; }  //For School Overflow  public int ClassId { get; set; }  public int MajorId { get; set; }  public int GradeId { get; set; }  //For comapany OverFlow  public int DepartmentId { get; set; }  public int ProjectId { get; set; }  }  

附加属性的本质就是依赖属性。二者仅在包装器和注册上有一点区别。前面已经讲过,VS里面自带的有用于快速创建依赖属性的snippet和propdp,现在使用另外一个snippet用于快速创建附加属性 propa。当VS出现高亮显示的时候连续按两次Tab键,一个附加属性框架就准备好了。继续按Tab键可以在几个空缺间轮换并修改,直至按下Enter键。

实例,使用School对Student添加附加属性,并弹出值:

    /// <summary>/// 附加属性 -- 基于特定的环境才会有的属性/// </summary>public class School:DependencyObject{public static int GetGrade(DependencyObject obj){return (int)obj.GetValue(GradeProperty);}public static void SetGrade(DependencyObject obj, int value){obj.SetValue(GradeProperty, value);}public static readonly DependencyProperty GradeProperty =DependencyProperty.RegisterAttached("Grade", typeof(int), typeof(School));}/// <summary>/// wnd73.xaml 的交互逻辑/// </summary>public partial class wnd73 : Window{Student stu = new Student();public wnd73(){InitializeComponent();School.SetGrade(stu, 6);string grade =  School.GetGrade(stu).ToString();MessageBox.Show(grade);}}

可以明显看出,GradeProperty就是一个DependencyProperty类型的成员变量,声明时一样使用了public static readonly修饰符。唯一的不同就是注册附加属性的时候使用的是RegisterAttached方法,但参数却与Register方法无异。附加属性的包装器与依赖属性的包装器不同---依赖属性使用CLR属性对GetValue和SetValue两个方法进行包装。附加属性则使用两个方法分别进行了包装----这样做完全是在使用的时候保持语句行文上的流畅。

WPF Property属性相关推荐

  1. 初步了解WPF依赖属性

    一 依赖属性 在WPF库实现中,依赖属性使用普通的C#属性进行了包装,使得我们可以通过和以前一样的方式来使用依赖属性.但必须明确,在WPF中我们大多数都在使用依赖属性,而不是使用属性.依赖属性重要性在 ...

  2. 定义一个属性_Python property属性

    1. 什么是property属性 一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法 # ############### 定义 ###############class Foo: def ...

  3. wpf 依赖属性和附加属性

    原文:wpf 依赖属性和附加属性 1.依赖属性 解释:依赖属性是配合binding出现的产物,功能主要是配合binding. 作用: 一.当自定义usercontrol时,需要与宿主(父窗体)双向绑定 ...

  4. 类 property属性

    类的定义 类的创建: 类是通过属性来保存数据的. 成员变量的修饰: Private:私有 外部不可访问 不可继承 Protected:被保护 外部不可访问 可继承 Public:公共 可被外部访问 可 ...

  5. python 基础教程:对 property 属性的讲解及用法

    Python中property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回 property属性的有两种方式: 装饰器 即:在方法上应用装饰器 类属性 即:在类中定义 ...

  6. python 中 property 属性的讲解及应用

    Python中property属性的功能是:property属性内部进行一系列的逻辑计算,最终将计算结果返回 property属性的有两种方式: 装饰器 即:在方法上应用装饰器 类属性 即:在类中定义 ...

  7. Python中菱形继承的MRO顺序及property属性

    Python中菱形继承的MRO顺序及property属性 文章目录 Python中菱形继承的MRO顺序及property属性 一.Python中菱形继承的MRO顺序 1. 单独调用父类的方法 2. 多 ...

  8. python中的property_python中的property属性

    1. 什么是property属性 一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法 # ############### 定义 ############### class Foo: def ...

  9. Python高级——property属性

    property属性 1.property属性: 是一个提高开发者用户体验度的属性,可以将一个函数改造的像属性一样. 例: # 定义的时候像是一个函数 使用的时候和属性的方式是以样的 class Fo ...

最新文章

  1. 获取App Store中App的ipa包
  2. C#:invoke 与 BeginInvoke使用区别
  3. eclipse mat 打开dump文件,明明大小1G,打开后却只有不到100M.其他的去哪了
  4. 20181030-4 每周例行报告
  5. 在web开发中,为什么前端比后端更得到转行程序员的青睐?
  6. 每日算法系列【LeetCode 684】冗余连接
  7. java 遍历文件夹 性能_Java函数式编程(十一):遍历目录
  8. 软件(自动化)测试面试基础知识点汇总
  9. 计算机休眠下睡眠的不同点是什么,电脑休眠和睡眠的区别
  10. vue二维码的批量生成及下载
  11. 一种人机友好的视频压缩方案(HMFVC)
  12. 伍伦贡计算机科学硕士申请,伍伦贡大学电脑科学(网络和信息安全)硕士研究生申请要求及申请材料要求清单...
  13. maven docker 部署到多台机器上。。_TensorFlow Serving + Docker + Tornado机器学习模型生产级快速部署
  14. 线程同步作业(一):Lock,monitor
  15. java:多线程的 共享资源冲突问题
  16. 通过server酱来发送爬虫爬到的数据
  17. DARPA举办AlphaDogfight决赛,AI 操控战斗机5:0战胜人类飞行员
  18. 社群运营 ≠ 微信客服
  19. sws_scale():bad dst image pointers
  20. 如何使用Android原生接口,实现“应用双开”

热门文章

  1. 网络名称空间netns的用法
  2. r7 4800u核显性能相当于什么显卡
  3. 基于JSP java家政服务管理系统
  4. 11月14日云栖精选夜读:轻松使用阿里云资源编排_方便你的API管理
  5. 「B站焊武帝」再出圈!孤身爆肝造CPU,软硬件全自研,可玩游戏,基础器件成本不到1000元...
  6. 漫画 | CPU就是一个悲惨的搬运工
  7. Python——提取符号、表格拆分数据(指定分隔符、分列)
  8. ios 内存深度优化_iPhone内存总是不够用?那是你不会清理,这样清立马腾出10G空间...
  9. 微信html5程序,H5跳转微信小程序
  10. python模拟足球射门_[转载]博客园仿真足球竞赛平台Python版SDK