我一直在网上看到示例,其中在方法中有一个元素的属性,在使用之前复制到局部变量。例如,像这样的东西(来自微软的StackPanel
源代码):
UIElementCollection children = arrangeElement.InternalChildren;
...
for (int i = 0, count = children.Count; i < count; ++i)
{
UIElement child = (UIElement)children[i];
if (child == null) { continue; }
...
}
任何人都可以向我解释这样做的好处是什么(如果有的话),而不是每次直接访问该属性,如下所示?:
for (int i = 0, count = arrangeElement.InternalChildren.Count; i < count; ++i)
{
UIElement child = (UIElement)arrangeElement.InternalChildren[i];
if (child == null) { continue; }
...
}
显然,它会在屏幕上保存一些字符,但这并不是理由。另外,我理解为什么我们可能希望使用长时间运行的方法来执行此操作,作为一种缓存形式:
double value = GetValueFromLongRunningMethod();
...
for (int i = 0; i < someCollection.Count; i++) DoSomethingWith(value);
但是我看到这对属性做了很多,并想知道为什么。这是互联网上与虚拟化有关的另一个常见例子:
IItemContainerGenerator generator = this.ItemContainerGenerator;
GeneratorPosition position = generator.GeneratorPositionFromIndex(firstVisibleItemIndex);
为什么要这样做呢?:
GeneratorPosition position =
this.ItemContainerGenerator.GeneratorPositionFromIndex(firstVisibleItemIndex);
最后,如果这样做是因为我们可能会缓存长时间运行的方法的结果,那么我们怎么知道需要以这种方式访问哪些属性?
答案 0 :(得分:6)
首先,它避免多次调用.InternalChildren
。这个可能是虚拟调用的一个小但明显的减少(因为它在循环中使用),但在某些情况下它可能更重要。在某些情况下,返回集合或数组的属性可能会在每次调用时分配 ; DataRow.ItemArray
是一个典型的例子 - 因此每次调用它都是主动有害的。另外一个考虑因素是,即使每次调用它都会返回相同的数组,也会出现JIT魔法,这种魔法恰好会忽略边界检查,但只有当JIT可以看到你正在为整个迭代迭代单个数组时它才会起作用。持续时间。如果你在中间粘贴一个属性访问器:这不会很明显,并且不会发生边界检查删除。如果您手动提升上限,也可能不会发生!
旁注:如果不是一个数组,那么foreach
可能通常更好,并且不会由于foreach
如何在内部工作,因此引入本地优势。
注意:由于您使用.Count
vs .Length
,这肯定不是数组,您应该简化为:
foreach(UIElement child = in arrangeElement.InternalChildren) {...}
或
foreach(var child = in arrangeElement.InternalChildren) {...}
这不仅完全消除了这个问题,而且意味着类型自己的迭代器(可能是一个优化的结构迭代器,或者可能是一个简单的IEnumerable<T>
类,例如编译器生成的迭代器块)可以使用。这通常可以更直接地访问内部,从而绕过索引器所需的一些间接和API检查。
答案 1 :(得分:1)
为什么要这样做呢?:
GeneratorPosition position = this.ItemContainerGenerator.GeneratorPositionFromIndex(firstVisibleItemIndex);
让我们对此非常抽象:
this.ItemContainerGenerator
,但这可能会改变。 当我们后来决定在其他地方使用该发生器时,用法应该保持不变。
这个例子太小了,无法令人信服,但这里有一些逻辑可供辨别。
答案 2 :(得分:1)
在某些情况下,如果必须
,这可能会很有成效