【问题标题】:C# Property Access OptimizationC# 属性访问优化
【发布时间】:2010-07-23 23:07:21
【问题描述】:

在 C#(或 VB .NET)中,编译器是否会尝试优化属性访问?例如,

public ViewClass View { get { ... Something is computed here .... } } if (View != null) View.Something = SomethingElse;

我想如果编译器能够以某种方式检测到View 在两次访问之间保持不变,它可以避免计算两次该值。是否执行了这些优化?

我知道如果View 有一些密集的计算,它可能应该被重构为一个函数(GetView())。在我的特殊情况下,View 涉及爬上可视化树以寻找特定类型的元素。

相关:关于(Microsoft)C# 编译器工作原理的任何参考资料?

【问题讨论】:

  • 编译器如何知道值没有改变?如果在多线程代码中使用该类怎么办?
  • @tster:想必,编译器有一个水晶球。

标签: c# .net optimization compiler-construction


【解决方案1】:

一般来说,不。正如 Steven 提到的,关于多线程有很多因素需要考虑,如果你真的在计算可能会改变的东西,那么你是对的,它应该从属性中重构出来。如果不改变,就懒加载(检查私有成员是否为null,如果是则计算,然后返回值)。

如果它不会改变并且取决于参数,您可以使用DictionaryHashtable 作为缓存 - 给定参数(键),您将存储该值。您也可以将每个条目作为值的WeakReference,因此当没有在任何地方引用该值并且发生垃圾收集时,内存将被释放。

希望对您有所帮助。

【讨论】:

  • 感谢您提出其他一些因素。
【解决方案2】:

这个问题很不清楚,对我来说,getter 和它下面的 sn-p 之间的关系并不明显。但是,是的,属性访问器通常会进行大量优化。不是由 C# 编译器,而是由 JIT 编译器。一方面,它们通常是内联的,因此您无需支付方法调用的成本。

只有当 getter 不包含太多代码并且不使用锁和异常处理时才会发生这种情况。您可以使用如下代码帮助 JIT 编译器优化常见情况:

get
{
    if (_something == null) {
        _something = createSomething();
    }
    return _something;
}

这将内联常见情况并允许创建方法保持非内联。这通常在发布版本(加载 + 测试 + 跳转)中编译为三个机器代码指令,执行时间约为纳秒。这是一个微优化,看到实际的性能改进是非常罕见的。

请注意,给定的示例代码不是线程安全的。总是先写正确的代码而不是快速的代码。

【讨论】:

  • 汉斯,你能否确认调用者是否必须在同一个程序集中才能内联属性?
  • @Steven,我可以确认不必必须在同一个程序集中。这会破坏内联 .NET 框架属性。
  • 很高兴知道。能发个参考链接吗?
  • @Steven,通过查看(优化的!)机器代码很容易确认。如果你不知道该怎么做,那么你可以问一个关于它的 SO 问题。
  • 我之所以问,是因为它以前在 SO 上出现过,但结果有些模棱两可,所以我想回去添加任何明确的参考来解决这个问题。无论如何,谢谢。
【解决方案3】:

不,这就是为什么您应该使用Lazy<T> 来实现 JIT 计算。

【讨论】:

    【解决方案4】:

    据我了解,没有隐式缓存 - 您必须在第一次计算时自己缓存给定属性的值

    例如:

      object mCachedValue = null;
        public Object MyProperty
        {
            get
            {
    
                if (mCachedValue == null)
                {
                   lock(mCachedValue)
                    {
                       //after acquiring the lock check if the property has not been initialized in the mean time - only calculate once
                        if (mCachedValue == null)
                        {
                            //calculate value the first time 
                        }
                    }
                }
                return mCachedValue;
            }
    

    【讨论】:

    • 这个想法是对的,但是这段代码是不对的。它缺乏线程安全性,这就是为什么我建议改用Lazy<T>
    • 你说得对,我没有考虑多线程。我修改了代码以考虑线程安全。
    • 不想让你失望,但这也不能保证有效。只需使用Lazy<T>。不要相信我的话:csharpindepth.com/Articles/General/Singleton.aspx