1. CLR 属性

.Net Framework 中的属性又称为 CLR 属性,是对 private 字段的安全访问包装。

使用 ILSpy 反编译器可以看到 C# 中代码的属性的编译结果是 set._xx 、get._xx 两个方法;

即使再多的实例,方法也只有一个拷贝,因此 CLR 属性并不会增加内存的负担;

语法糖衣。

2. 依赖属性 propdp

2.1 比 CLR 属性的优势:

节省实例对内存的开销;

属性值可以通过 Binding 依赖在其他对象上;

每个类在实例的时候,会为各个非静态字段开辟一块内存,而这些字段并非都会被用到,这就造成对内存的浪费;

而依赖属性在需要用到时能够获取默认值、借用其他对象数据或实时分配空间;

  1. 必须使用依赖对象作为依赖属性的宿主。
namespace System.Windows
{
public class DependencyObject : DispatcherObject
{
public object GetValue(DependencyProperty dp);
public void SetValue(DependencyProperty dp, object value);
}
} public class Student : DependencyObject
{
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(Student));
}
  1. public static readonly,命名规定变量名字加上 Property 表明是一个依赖属性;
  2. DependencyProperty.Register("Name", typeof(string), typeof(Student)) 中第 1 个参数指明以哪个 CLR 属性作为这个依赖属性的包装器,目前虽然没有为这个依赖属性准备包装器,将来会使用名为 Name 的 CLR 属性来包装它;
  3. 第 2 个参数表示存储什么类型的值,第 3 个表示宿主的类型;
Student stu = new Student();
new System.Threading.Thread(() =>
{
while(true)
{
System.Threading.Thread.Sleep(1000);
App.Current.Dispatcher.Invoke(() =>
{
stu.SetValue(Student.NameProperty, DateTime.Now.ToString());
});
}
}).Start(); Binding bd = new Binding("Name") { Source = stu };
this.textBlock.SetBinding(TextBox.TextProperty, bd);

这样,textBlock 就会实时展示当前时间;因此依赖属性天生就是合格的数据源,不需要实现 INotifyPropertyChanged 接口就能在属性的值发生改变时通知与之关联的 Binding 对象;

  • 再次强调,有没有包装器(CLR 属性),这个依赖属性(DependencyProperty)都存在;
  • 使用包装器:
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value);}
} stu.SetValue(Student.NameProperty, DateTime.Now.ToString());
=
stu.Name = DateTime.Now.ToString();
  • 如果作死,DependencyProperty.Register 方法第一个参数跟包装器的名称不一致,那么使用 Binding 时,实例化 Binding 传入的参数是 DependencyProperty.Register 第一个参数名称,而不是包装器的名称;
  • DependencyProperty.Register 第 4 个参数 new PropertyMetadata() 用于依赖属性未被显式赋值时,若读取之则获得此默认值。

2.2 依赖属性值存取的秘密

被 static 关键字所修饰的依赖属性对象其作用是用来检索真正的属性值,而不是存储值。

源码:

class DependencyProperty
{
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata, ValidateValueCallback validateValueCallback)
{
...
DependencyProperty dependencyProperty = RegisterCommon(name, propertyType, ownerType, defaultMetadata, validateValueCallback);
...
return dependencyProperty;
} private static DependencyProperty RegisterCommon(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
FromNameKey key = new FromNameKey(name, ownerType);
...
DependencyProperty dependencyProperty = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, validateValueCallback); lock (Synchronized)
{
PropertyFromName[key] = dependencyProperty;
}
...
return dependencyProperty;
} private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback validateValueCallback)
{
...
Flags flags;
lock (Synchronized)
{
flags = (Flags)GetUniqueGlobalIndex(ownerType, name);
RegisteredPropertyList.Add(this);
}
...
_packedData = flags;
} private static Hashtable PropertyFromName = new Hashtable(); public override int GetHashCode()
{
return GlobalIndex;
} public int GlobalIndex => (int)(_packedData & Flags.GlobalIndexMask);
} class DependencyObject
{
EffectiveValueEntry[] _effectiveValues;
public object GetValue(DependencyProperty dp)
{
...
return GetValueEntry(LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value;
} internal EffectiveValueEntry GetValueEntry(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, RequestFlags requests)
{
...
EffectiveValueEntry entry = ((requests & RequestFlags.RawEntry) == 0) ? GetEffectiveValue(entryIndex, dp, requests) : _effectiveValues[entryIndex.Index];
result = (entryIndex.Found ? entry : new EffectiveValueEntry(dp, BaseValueSourceInternal.Unknown));
...
return result; // 便于理解简化成
return _effectiveValues[entryIndex.Index];
}
}
  1. DependencyProperty 注册时,会根据 CLR 属性名称和宿主类型名称各自 hash 之后做异得到一个 key,然后从 PropertyFromName 从判断是否存在相同的 key,如果还没有,就实例化一个 DependencyProperty 并与 key 作为键值对保存到 PropertyFromName,最后才返回实例化的对象。这个对象有着一个唯一 GlobalIndex;
  2. 每个 DependencyObject 都有一个 EffectiveValueEntry 数组,EffectiveValueEntry 可以理解为一个小房间,每个存在的 EffectiveValueEntry 的 PropertyIndex 属性的值为某个 DependencyProperty 的 GlobalIndex,因此,DependencyProperty 可以理解为一个 key,需要用时,在 EffectiveValueEntry[] 中根据 GlobalIndex 取或赋值 EffectiveValueEntry 的 value 属性的值.
  3. 至此,也就解释了为什么 public static readonly DependencyProperty NameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Student)); 是 static 而每个对象都可以往里面"存""取"各自的值,因为它只是一个 key,而不是 value。使用 readonly 是为了保持稳定性。

3. 附加属性 propa

3.1 讲解

<TextBlock Grid.Row="1"/>
<TextBlock Canvas.Top="30"/>
<TextBlock DockPanel.Dock="Left"/>
  1. Row 是 Grid 的属性,Top 是 Canvas 的属性,Dock 是 DockPanel 的属性,TextBlock 并没有准备这些属性;
  2. 附加属性的作为将属性和数据类型(宿主)解耦;
  3. 附加属性的本质也是依赖属性
public class Student : DependencyObject
{
public static int GetMyProperty(DependencyObject obj)
{
return (int)obj.GetValue(MyPropertyProperty);
} public static void SetMyProperty(DependencyObject obj, int value)
{
obj.SetValue(MyPropertyProperty, value);
} public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(Student), new PropertyMetadata(0));
} public class Human : DependencyObject {} Human human = new Human();
Student.SetMyProperty(human, 6);
int value = Student.GetMyProperty(human);

跟依赖属性保存值一样,值依然被保存在 Human 实例的 EffectiveValueEntry 数组中,只是用于在数组中检索值的依赖属性(即附加属性)并不以 Human 类为宿主而是寄宿在 Student 中。关系不大,反正 CLR 属性名和宿主类型名只用来生成 hash code 和 GlobalIndex。

3.2 例子

3.2.1 例子 1
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1"/>
</Grid> Grid grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star)});
grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto}); TextBlock tb = new TextBlock();
Grid.SetRow(tb, 1); grid.Children.Add(tb);
3.2.2 例子 2
<Slider x:Name="sliderX" Minimum="0" Maximum="300"/>
<Slider x:Name="sliderY" Minimum="0" Maximum="300"/>
<Canvas Height="50" Width="500" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Yellow">
<Rectangle x:Name="rect" Fill="Red" Width="30" Height="30"
Canvas.Left="{Binding ElementName=sliderX, Path=Value}"
Canvas.Top="{Binding ElementName=sliderY, Path=Value}" />
</Canvas>

等价于

<Slider x:Name="sliderX" Minimum="0" Maximum="300"/>
<Slider x:Name="sliderY" Minimum="0" Maximum="300"/>
<Canvas Height="50" Width="500" HorizontalAlignment="Left" VerticalAlignment="Top" Background="Yellow">
<Rectangle x:Name="rect" Fill="Red" Width="30" Height="30"/>
</Canvas> this.rect.SetBinding(Canvas.LeftProperty, new Binding("Value") { Source = this.sliderX});
this.rect.SetBinding(Canvas.TopProperty, new Binding("Value") { Source = this.sliderY});

WPF 基础 - 属性的更多相关文章

  1. WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)

    一. 摘要 首先圣殿骑士非常高兴这个系列能得到大家的关注和支持.这个系列从七月份開始到如今才第七篇,上一篇公布是在8月2日,掐指一算有二十多天没有继续更新了,最主要原因一来是想把它写好,二来是由于近期 ...

  2. WPF基础到企业应用系列6——布局全接触

    本文转自:http://knightswarrior.blog.51cto.com/1792698/365351 一. 摘要 首先很高兴这个系列能得到大家的关注和支持,这段时间一直在研究Windows ...

  3. WPF 基础到企业应用系列索引

    转自:http://www.cnblogs.com/zenghongliang/archive/2010/07/09/1774141.html WPF 基础到企业应用系列索引 WPF 基础到企业应用系 ...

  4. WPF笔记(1.1 WPF基础)——Hello,WPF!

    原文:WPF笔记(1.1 WPF基础)--Hello,WPF! Example 1-1. Minimal C# WPF application// MyApp.csusing System;using ...

  5. WPF 依赖属性前言

    WPF 依赖属性前言 ​ 在.net中,我们可以属性来获取或设置字段的值,不需要在编写额外的get和set方法,但这有一个前提,那就是需要在对象中拥有一个字段,才能在此字段的基础上获取或设置字段的值, ...

  6. WPF中属性经动画处理后无法更改的问题

    在WPF的Animation中,有一个属性为FillBehavior,用于指定时间线在其活动周期结束后但其父时间线仍处于活动周期或填充周期时的行为方式.如果希望动画在活动周期结束时保留其值,则将动画F ...

  7. WPF依赖属性详解

    WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...

  8. wpf 依赖性属性

    1 依赖性属性的作用 在WPF体系中,只有定义属性为依赖项属性,这个属性才支持样式设置,数据绑定,继承,动画和默认值.也就是 这个属性才能具有WPF中的一些特点. 它支持自动通知UI控件. WPF的属 ...

  9. Unity UGUI——Rect Transform组件(基础属性)

    基础属性:Width.Height.Pivot图示 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJfQUhhbw==/font/5a6L5L2T/fo ...

随机推荐

  1. leetcode8 字符串转换整数

    <cctype> isdigit(char) 问题:在做乘法,加法前,先判断是否溢出 &&优先级大于== 然后教训: 考虑情况不周.比如3.14这样 然后解决办法 多自己搞 ...

  2. springboot demo(一)快速开始

    快速入门 maven构建项目 1.访问http://start.spring.io/ 2.选择构建工具Maven Project.Spring Boot版本2.26以及一些工程基本信息,点击" ...

  3. zhihu level

    zhihu level https://www.zhihu.com/creator/account/growth-level refs xgqfrms 2012-2020 www.cnblogs.co ...

  4. The Weekly Web Dev Challenge: Emoji Ratings

    The Weekly Web Dev Challenge: Emoji Ratings /* DESCRIPTION: You job is to enable users to give a rat ...

  5. TypeScript & React & Jest

    TypeScript & React & Jest create-react-app Jest ``tsx import React from 'react'; import { re ...

  6. 视屏剪辑软件 & free video editor

    视屏剪辑软件 & free video editor purpose add animation keyframe to tutorials video vlog demos tutorial ...

  7. element ui 停止维护了

    ️‍♂️ element ui 停止维护了 最近看到有人说 element ui 已经停止维护了,还有点不相信; 不过到 github 验证一下,好像是真的呀 4 个月,没有任何更新了 https:/ ...

  8. SPC算力币异军突起,或将跑赢币圈全场

    比特币在出现反弹以后,并没有向上突破,而是回调,目前已经跌破了35000美金.目前,整个市场都在关注着比特币的动向,毕竟,比特币的走势,关注着资本的流向.不过,也有一部分巨鲸们将目光对准了币圈的其它数 ...

  9. PAUL ADAMS ARCHITECT:澳洲房贷最低利率来袭

    11月3日澳洲储备银行宣布将官方现金利率从0.25%降至0.1%,破历史最低纪录.此次澳洲储备银行降息的目的主要是为了刺激经济走出全球经济危机引发的衰退.据了解,这已经是澳洲今年第三次降息,也是自20 ...

  10. 基于tcp的应用层消息边界如何定义

    聊聊基于tcp的应用层消息边界如何定义 背景 2018年笔者有幸接触一个项目要用到长连接实现云端到设备端消息推送,所以借机了解过相关的内容,最终是通过rabbitmq+mqtt实现了相关功能,同时在心 ...