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. VMX - block by NMI和 NMI unblockinig due to IRET 之间的关系

    相关SDM章节: 27.2.3- Information About NMI Unblocking Due to IRET 最近收到同事发来的一个问题,即: VMCS 中的 Guest Interru ...

  2. Linux系统诊断必备技能之二:日志查询常用命令详解

    一.概述 日常运维工作中,排查线上环境问题,少不了去线上查日志.而使用什么命令,能快速准确地查到我们需要查找地日志信息,也是我们需要掌握的一项技能.下面介绍一下日常工作常用到的查看日志命令:tail, ...

  3. Pymongo 笔记

    Pymongo 1.MongoDB概念 MongoDB是一种非关系型数据库(NoSQL),MongoDB数据存储于内存,内存不足则将热度低数据写回磁盘.存储的数据结构为文档.每个数据库包含若干集合(c ...

  4. pikachu-反射性xss(get)

    首先打开漏洞网页,发现输入的长度好像被限制了, 我们便F12查看源代码,发现长度被限制成了20,而且还是前端验证的,我们可以直接修改为100 在我们的输入框中,输入 <script>ale ...

  5. range()函数的使用、while循环、for-in循环等

    一.range()函数 用于直接生成一个整数序列 创建range对象的三种方式: (1)range(stop)    创建一个(0,stop)之间的整数序列,步长为1 (2)range(start,s ...

  6. print函数与转义字符

    一.print函数 作用:1.输出数字. print(520) 2.输出字符串.(注意:输出字符串要添加单引号.双引号或三引号,告诉编译器可直接输出,不用分析) print('you are so b ...

  7. uni-app 支持第三方 H5 离线包

    uni-app 支持第三方 H5 离线包 https://uniapp.dcloud.io/ https://github.com/dcloudio/uni-app refs xgqfrms 2012 ...

  8. 加密算法大全图解 :密码体系,对称加密算法,非对称加密算法,消息摘要, Base64,数字签名,RSA,DES,MD5,AES,SHA,ElGamal,

    1. 加密算法大全: ***************************************************************************************** ...

  9. WebIDE All In One

    WebIDE All In One web IDE Visual Studio Code vscode Code editing Redefined. Free. Built on open sour ...

  10. taro taro 多端同步调试

    taro 多端同步调试 debug https://nervjs.github.io/taro/docs/envs-debug.html