揭开依赖属性的神秘面纱

San*_*box 18 .net silverlight wpf dependency-properties

我在SO和其他网站上已经阅读了很多依赖属性.但是,真的没有找到一个很好的解释,我仍然感到困惑.我同时使用SL和WPF.在实施方面,它们在SL和WPF方面有所不同吗?为什么我们真的需要它们?它们是否静态意味着它们的价值是共享的?为什么MS引入依赖属性的原因?

赏金: 我正在寻找一个更彻底,更完整的答案.

Rob*_*ney 19

答案在于名称本身,尽管"依赖"这个词在软件开发中如此充满意义,但这并不是特别清楚这意味着什么.

一个依赖属性是一个对象,其值在属性依赖于某些其他对象.所以,例如:

  • can 的FontFamily属性值TextBox(通常是)取决于FontFamily其容器的属性.如果更改容器上的属性,则TextBox更改值.

  • Texta 的属性值TextBox可以取决于绑定的数据源.当绑定属性的值更改时,属性的值会Text更改.

  • Opacitya 的属性值Label取决于动画故事板,在常见的场景中,您已设置UI元素以淡入或淡出以响应某些事件.

  • 任何UI元素上的各种属性的值可以取决于您应用于它们的样式.

依赖性背后的核心概念是,依赖的东西应该从它所依赖的东西中获取属性值.这就是将依赖项属性实现为其getter调用方法的CLR属性的原因.

TextBox那的渲染,或东西,需要知道它是什么FontFamily就是,FontFamily吸气调用GetValue方法.该方法从容器,动画或绑定或其他任何内容中查找值.

这种方法有很多复杂性.例如,如果值继承,它的工作方式非常类似于WPF在资源字典中查找样式的方式:它在本地字典中查找以查找值,如果没有条目则在其父字典中查找,并且所以一直到它找到一个值或到达层次结构的顶部,在这种情况下它使用默认值.

如果你看一下依赖属性的实现,那就是你会发现的.依赖项对象具有可能包含或不包含给定属性的条目的字典.该GetValue方法从该字典中获取值(具有依赖项属性的对象可以具有覆盖它们继承的本地值的方式),如果它没有找到该值,则使用有关该属性的元信息来确定应该看.

由于该元信息对于类中的每个对象都是相同的(即,对于每个对象都是TextBox.Text相同的TextBox),因此存储的字典是类的静态属性.

所以当你看到这样的代码时:

static Button()
{
   // Register the property
   Button.IsDefaultProperty = 
     DependencyProperty.Register("IsDefault",
     typeof(bool), typeof(Button),
     new FrameworkPropertyMetadata(false,
        new PropertyChangedCallback(OnIsDefaultChanged)));
}
Run Code Online (Sandbox Code Playgroud)

发生的事情是,IsDefault在所有Button对象上定义属性的元信息被添加到该字典中.当你看到这个:

public bool IsDefault
{
  get { return (bool)GetValue(Button.IsDefaultProperty); }
  set { SetValue(Button.IsDefaultProperty, value); }
}
Run Code Online (Sandbox Code Playgroud)

你所看到的是基于该元信息查找属性值(来自本地字典,父对象或其他)的getter方法.

还记得我是怎么说getter看起来找到属性值的第一个位置是对象的本地字典吗?SetValuesetter中的方法是如何将该条目添加到字典中(如果它曾被调用过,只有当你通过显式设置属性来覆盖依赖项时才会这样,即说"我想让它TextBox显示文本Consolas而不管它是什么"窗口中的其他控件正在使用什么.").

我们从这种看似复杂的系统中获得的一个非常重要的好处是,对象的依赖属性只有在设置时才会消耗内存.如果我创建10,000个TextBox控件并将它们添加到a Window,则其中没有一个实际包含FontFamily对象的引用.这是10,000个对象引用,我没有为其分配内存,并且垃圾收集器没有检查.事实上,如果一个TextBox拥有100个依赖属性(并且确实如此),那么无论何时创建一个TextBox,那就是100个支持字段,而不是为其分配内存.如果您明确设置它们,依赖项属性仅消耗内存.由于UI对象上绝大多数属性都没有明确设置,因此这些都是非常节省的.


Jam*_*acs 11

其他人提供了依赖属性的解释.我将尝试提供有关其设计的一些背景信息.(它帮助我理解为什么这些奇怪的创建称为依赖属性首先存在.)WPF的设计者添加依赖属性而不是使用普通属性有几个原因...

  1. 能够为不适用于当前元素的属性添加值,但适用于其子元素.例如,在容器上设置字体并将其级联到任何包含的文本元素.
  2. 未使用属性的大小成本.以上面的字体为例......在设计良好的页面上,只有少数元素会修改其字体 - 可能是一些顶级容器.如果我们将Font存储为常规C#属性,则每个对象都需要一个32/64位指向字体对象的指针,其中大多数为null或具有默认值.通过XAML属性的数量和页面上的典型元素数量来放大这一点,最终需要大量空间来存储任何内容或默认值.将此与依赖项属性进行比较,其中值仅在设置时占用空间.(我没有看过内部实现,但我想GetValue(propName)和SetValue(propName)将值存储在某种per-object/per-propety哈希表中.)

如果Microsoft通过使用编译器魔术隐藏重复代码来提供创建新依赖项属性的更好语法,就像它们对迭代器一样,那将是很好的.也许在C#6 ...... :)