ElementName和RelativeResource有什么区别?

20
以下哪个TextBlock绑定会导致性能损失更大:
<Window  
  x:Name="Me"
  x:Class="MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:src="clr-namespace:WpfApplication1" 
  Title="MainWindow">
  <StackPanel>
    <TextBlock Text="{Binding Title, ElementName=Me}"/>
    <TextBlock Text="{Binding Title, RelativeSource={RelativeSource AncestorType={x:Type src:MainWindow}}}"/>
  </StackPanel>    
</Window>

我相信当TextBlocks嵌套层级很高并且拥有许多兄弟和祖先时,我的问题可能会有所不同。

注意事项

(仅基于个人想法,每个特定的想法可能都是错误的!):

  • ElementName:

    • 可能会通过所有子元素、同级元素、叔叔和曾祖父(可能存在所有已注册名称的哈希表)将当前元素与更多控件进行搜索和比较,包括祖先。
    • 获取控件的Name属性应该比调用GetType更具有性能优势。
    • 与比较类型相比,比较字符串更加便宜,特别是当您知道大多数控件甚至没有设置它们的Name时。
  • FindAncestor:

    • 只会迭代祖先,而不是兄弟姐妹、叔叔阿姨等。
    • 最可能使用GetType来确定祖先类型;GetType的成本比简单的Name属性getter更高(也许DP不同?)。

3
注意:这段文字与性能和主题无关(但仍涉及ElementNameRelativeSource):请记住,ElementName是**'早期绑定',而RelativeSource'延迟绑定'**,这可能会导致意外的结果,就像我在这个问题中展示的那样:https://dev59.com/4HbZa4cB1Zd3GeqPBQ_r。 - lxa
3个回答

32
通常争论哪种方法更快是一个糟糕的想法,最好的方法是构建一个实验来测量它。
我修改了您的设置 - 我将相关的Xaml放入了UserControl中,并绑定到“Name”属性,因为“UserControl”没有“Title”属性。然后,我编写了一些代码来创建控件的新实例并将其添加到UI中,并使用Stopwatch来测量构造和加载所需的时间。(我在构造用户控件之前开始计时,只有在用户控件引发“Loaded”事件后才停止计时。)
我从“DispatcherTimer”每秒运行20次这个代码,以便我可以进行大量测量,以期减少实验误差。为了最小化由于调试和诊断代码产生的畸变,我在发布版本中运行,并且仅在完成2000次迭代后计算和打印平均值。
经过2000次迭代,“ElementName”方法平均需要887us。
经过2000次迭代,“RelativeSource”方法平均需要959us。
因此,在这个特定的实验中,“ElementName”比“RelativeSource”略快。当加载一个仅有一个命名元素的微不足道的“UserControl”,其中只有一个“Grid”和一个“TextBlock”时,“ElementName”方法看起来需要的时间约为“RelativeSource”的92%。
当然,我在这里测量了一个小的人造示例。ElementName方法的性能可能会根据作用域中有多少命名元素而变化。在实际情况下,可能有其他未预料到的因素会产生完全不同的结果。因此,如果您想获得更好的图片,我建议在实际应用程序的上下文中执行类似的测量。

我用了10个TextBlock而不是只用一个,重复了一遍实验。这时ElementName的平均值为2020微秒,而RelativeSource方法的平均值为2073微秒,两种测试都进行了2000次迭代。奇怪的是,差别在这里更小了,不仅相对而言,而且绝对上也如此——单元素示例的差异为72微秒,而十元素示例的差异为53微秒。

我开始怀疑是我在我的主机上运行测试导致了更多的可变性,而不是在一个精心配置、尽可能减少噪音的机器上运行测试。

再提供一个变量:仍然有10个绑定文本块,我又添加了十个空的、未绑定的命名文本块到用户控件中。这里的想法是引入更多的命名物品——ElementName现在必须在11个命名的物品中找到一个命名的项目。现在ElementName的平均值为2775微秒。使用这些额外的10个命名元素的RelativeSource方法的平均值为3041微秒。

同样地,我怀疑这里的可变性在我的桌面机上——看起来RelativeSource在这里表现明显比在应该更有利于ElementName的情况下表现更差。

总之,似乎清晰明了的是,这里加载的成本对元素数量的敏感度要比使用哪种绑定风格高得多。使用ElementName似乎略有优势,但足够小(而且结果也很奇怪),以至于对于结论它是否必然更快速,存在怀疑。

所以我们可以进行更仔细的实验,以获得更好的图片。但在我看来,如果你不能在普通计算机上明确地展示性能的显著差异,那就浪费时间去争论哪个更快。

因此,总的来说:在这里关注性能是错误的。选择使代码更易于阅读的方法。


很棒的帖子。 :) 我对性能差异非常好奇。但是,专注于哪个可以产生更可读的XAML是有道理的。 :) - Ashley Grenon
感谢Ian在这个出色的答案中所做的努力! - Shimmy Weitzhandler

4
两者中的后者需要遍历可视树来查找特定祖先类型,而前者直接查找窗口名称范围内已注册对象的名称...我猜后者可能稍微慢一些...话虽如此,我认为性能差异不会很大。
希望对您有所帮助,
Aj

1
有道理,但问题是如果名称有太多的兄弟姐妹、叔叔阿姨和曾祖父母,那么对于每个级别,它都会搜索所有这些人,而使用FindAncestor则不会查找任何东西,只会查找祖先。另一方面,我认为获取每个祖先的类型(GetType)本身可能比仅获取兄弟姐妹的名称要花费更长的时间,更不用说许多控件没有命名,字符串比较甚至更快了。如果您能依赖于像反射器、MSDN等真实的来源来回答,我将不胜感激。 - Shimmy Weitzhandler
@Shimmy,我认为适当的测试会相当困难,至少要产生准确和一致的结果。老实说,我认为这超出了我的能力范围,因此我不想发布比受过教育的猜测更多的内容。顺便说一下,我读到了Josh Smith撰写的一篇文章,其中讨论了逻辑树和可视树;虽然不涉及性能差异,但他包括了一个遍历可视和逻辑树的示例应用程序。也许你可以使用它来获取有关你的查询的一些见解- http://www.codeproject.com/KB/WPF/WpfElementTrees.aspx - Aaj

1
一般情况下,应尽可能使用ElementName。给定的示例和基准示例非常简单。在实际示例中,元素具有更大的可视树,FindAncestor绑定必须遍历更多元素才能找到元素。通过将一些FindAncestor绑定更改为ElementName绑定,在真实世界的应用程序中,我节省了几秒钟。在我看来,ElementName绑定更易读。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接