C# 属性访问优化

问题描述 投票:0回答:4

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


public ViewClass View
{
    get
    {
        ...
        Something is computed here
        ....
    }
}

if (View != null)
    View.Something = SomethingElse;


我想,如果编译器能够以某种方式检测到

View
在两次访问之间保持不变,它就可以避免两次计算该值。是否进行了此类优化?

我知道如果

View
有一些密集的计算,它可能应该被重构为一个函数(
GetView()
)。在我的特定情况下,
View
涉及攀登视觉树寻找特定类型的元素。

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

c# .net optimization
4个回答
5
投票

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

如果它不会改变并且取决于参数,您可以使用

Dictionary
Hashtable
作为缓存 - 给定参数(键),您将存储该值。 您也可以将每个条目作为该值的
WeakReference
,因此当该值在任何地方都没有被引用并且发生垃圾收集时,内存将被释放。

希望有帮助。


2
投票

这个问题非常不清楚,我不清楚 getter 和它下面的代码片段是如何相关的。 但是,是的,属性访问器通常经过大量优化。 不是由 C# 编译器执行,而是由 JIT 编译器执行。 其一,它们通常是内联的,因此您无需支付方法调用的成本。

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

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

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

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


1
投票

不,这就是为什么你应该使用

Lazy<T>
来实现 JIT 计算。


0
投票

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

例如:

  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;
        }
© www.soinside.com 2019 - 2024. All rights reserved.