【问题标题】:What is the benefit of using a local variable?使用局部变量有什么好处?
【发布时间】:2018-05-04 10:34:49
【问题描述】:

我一直在网上看到示例,其中方法中的元素属性在使用前被复制到局部变量中。例如,像这样的东西(来自微软的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);

最后,如果这样做的原因与我们可能缓存长时间运行的方法的结果相同,那么我们应该如何知道需要以这种方式访问​​哪些属性?

【问题讨论】:

  • 可读性....
  • 你说的是online examples,所以答案是可读性。
  • 如果该物业被调用昂贵怎么办?请记住,属性是方法...
  • 更容易读、写,而且通常性能更好。 (这是一个小问题)
  • 如果我错了,请纠正我,但考虑到一些没有相关成本的属性/字段链并且分配了局部变量并且从未修改过,因此它只是一个额外的参考,具有可读性作为唯一的好处,我认为编译器会优化它来抵消它的成本

标签: c# variables properties


【解决方案1】:

首先,它避免了多次调用.InternalChildren。这可能是虚拟调用的小幅但明显的减少(因为它是在循环中使用的),但在某些情况下它可能更重要。在某些情况下,返回集合或数组的属性可能会在每次调用时分配DataRow.ItemArray 是一个典型的例子 - 所以每次调用它是积极有害的。另一个考虑因素是,即使每次调用它都返回相同的数组,也有 JIT 魔法可以避免边界检查,但只有当 JIT 可以看到您正在为整个迭代单个数组时它才会起作用期间。如果您在中间插入一个属性访问器:这不会很明显,并且不会发生边界检查删除。如果您手动提升了上限,也可能不会发生!

旁注:如果它不是一个数组,那么foreach可能通常更可取,而不会由于foreach 在内部的工作方式,引入本地人有什么好处。


注意:由于您使用的是 .Count.Length,这绝对不是一个数组,您应该简化为:

foreach(UIElement child = in arrangeElement.InternalChildren) {...}

foreach(var child = in arrangeElement.InternalChildren) {...}

这不仅完全消除了这个问题,而且意味着该类型自己的迭代器(可能是优化的结构迭代器,也可能是简单的IEnumerable&lt;T&gt; 类,例如编译器生成的迭代器块)用过的。这通常可以更直接地访问内部,从而绕过索引器所需的一些间接和 API 检查。

【讨论】:

  • @bommelding 确实;我在谈论 arrangeElement.InternalChildrenchildren 的用法,但你是绝对正确的:如果这是一个数组,我们不应该尝试优化 .Length 的用法,但是:也许事实上它们是使用 .Count 而不是 .Length 告诉我们它绝对不是一个数组......将添加一个编辑
  • 感谢您的解释,马克。你提出了一些有趣的观点。但我想提醒您,该代码来自StackPanel 类,不是我编写的。
  • @WPFguy 我试图用最笼统的术语来回答,不依赖于谁编写了代码,或者确切的场景是什么......
【解决方案2】:

在某些情况下可能会很有成效,比如当你不得不这样做时

  • 调试一段代码,你需要立即看到变量的值
  • 一次对一个对象执行一些操作,这需要强制转换 - 结果你只强制转换一次
  • 有时,当您使用值类型对象时,这种制作本地副本的方式使您有机会不更改类属性的值

【讨论】:

    【解决方案3】:

    为什么这样做而不是这个?:

    GeneratorPosition position =
     this.ItemContainerGenerator.GeneratorPositionFromIndex(firstVisibleItemIndex);
    

    让我们对此非常抽象:

    1. 我们得到了一台发电机。现在显然是this.ItemContainerGenerator,但这可能会改变。
    2. 我们使用它。这里只有一次,但通常在多个语句中。

    当我们后来决定将那个生成器放到别处时,用法应该保持不变。

    这个例子太小了,不足以让人信服,但这里有一些逻辑需要辨别。

    【讨论】:

    • 这是一个公平的观点。但是针对可能的情况进行编程通常不是一个好主意,不是吗?
    • 很多架构都针对可能发生的变化。而且它们总是会发生。
    • 感谢您的回答。我没想到。
    猜你喜欢
    • 2020-10-25
    • 2018-02-20
    • 2010-12-15
    • 1970-01-01
    • 2011-03-14
    • 2017-12-30
    • 2013-05-25
    • 1970-01-01
    • 2013-09-16
    相关资源
    最近更新 更多