在 C#(或 VB .NET)中,编译器是否尝试优化属性访问?例如,
public ViewClass View
{
get
{
...
Something is computed here
....
}
}
if (View != null)
View.Something = SomethingElse;
我想,如果编译器能够以某种方式检测到
View
在两次访问之间保持不变,它就可以避免两次计算该值。是否进行了此类优化?
我知道如果
View
有一些密集的计算,它可能应该被重构为一个函数(GetView()
)。在我的特定情况下,View
涉及攀登视觉树寻找特定类型的元素。
相关:有关 (Microsoft) C# 编译器工作原理的任何参考吗?
一般来说不是,不是。 正如史蒂文提到的,关于多线程有很多因素需要考虑,如果您确实正在计算可能会改变的东西,那么您是对的,应该将其从属性中重构出来。 如果它不会改变,你应该延迟加载它(检查私有成员是否为空,如果是则计算,然后返回值)。
如果它不会改变并且取决于参数,您可以使用
Dictionary
或 Hashtable
作为缓存 - 给定参数(键),您将存储该值。 您也可以将每个条目作为该值的WeakReference
,因此当该值在任何地方都没有被引用并且发生垃圾收集时,内存将被释放。
希望有帮助。
这个问题非常不清楚,我不清楚 getter 和它下面的代码片段是如何相关的。 但是,是的,属性访问器通常经过大量优化。 不是由 C# 编译器执行,而是由 JIT 编译器执行。 其一,它们通常是内联的,因此您无需支付方法调用的成本。
只有当 getter 不包含太多代码并且不使用锁和异常处理时,才会发生这种情况。 您可以使用如下代码帮助 JIT 编译器优化常见情况:
get
{
if (_something == null) {
_something = createSomething();
}
return _something;
}
这将内联常见情况并允许创建方法保持非内联状态。 这通常会在发布版本中编译为三个机器代码指令(加载+测试+跳转),执行时间约为纳秒。 这是一个微观优化,看到实际的性能改进是非常罕见的。
请注意,给定的示例代码不是线程安全的。 始终首先编写正确的代码而不是快速的代码。
不,这就是为什么你应该使用
Lazy<T>
来实现 JIT 计算。
根据我的理解,没有隐式缓存 - 您必须在第一次计算给定属性的值时自己缓存它
例如:
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;
}