我有一个包含公共属性的类。在一个函数中,我大约会引用这个属性30-40次。
this.MyProp;
在函数中定义一个局部变量是否更好?
string myProp = this.MyProp;
在这个过程中,我缩短了查找链,所以在函数中只需要引用myProp,而不是this.MyProp。在JavaScript中,这种查找链的缩短确实可以提高性能。但是在C#中会更好还是更差呢?因为显然,我还需要创建另一个本地字符串变量。
我有一个包含公共属性的类。在一个函数中,我大约会引用这个属性30-40次。
this.MyProp;
在函数中定义一个局部变量是否更好?
string myProp = this.MyProp;
从纯性能角度来看,这要取决于属性的作用。如果MyProp
getter只是返回一个私有字段,那么开销就完全可以忽略不计。实际上,除非你明显在做一些重要的事情,比如一些巨大的枚举或调用数据库,否则这并不重要。担心这个问题是微观优化。
但需要注意的是属性的目的是通过封装的程序强制访问值以确保一致性。如果尝试绕过这一点,会使代码面临更大的错误风险。
根据@DavidAndres评论:
按照惯例,属性通常不应在getter或setter中执行任何实质性操作。因此,如果仅验证属性表现良好,可以通过反射器、文档或查看自己的代码等方式,那么这个问题就无需关注。只有在已经验证有必要时,处理明显需要本地化值的情况才是边缘情况。
编辑说明:需要澄清的是,我并不建议您出于任何性能原因普遍避免使用本地值。正如@Guffa所指出的,影响可以忽略不计。我想指出的是属性通常有其存在的原因,访问值应默认经过属性。
不要害怕声明本地变量,这几乎是免费的。在32位系统上,字符串变量将使用四个字节的堆栈空间。分配局部变量的堆栈帧总是被创建的,因此分配变量不需要额外的执行时间。
然而,是否应该使用局部变量应该基于情况中更正确的选择。除非你有一个循环,在其中你使用像一千次或更多这样的值,否则性能差异甚至几乎无法测量。
(当然,这是基于假设属性已经正确实现,所以读取属性并不昂贵。)
对于简单的函数和常规属性,这应该不会让你担心。
然而,这似乎是一个很好的关于lambda表达式的问题,快速测试表明如果你有很多属性引用(这类似于这里所做的),那么它可能是值得的:
class Program
{
public static int Property { get; set; }
static void Main(string[] args)
{
Property = 3;
int mainVar = Property;
for (int run = 0; run < 5; run++)
{
Stopwatch s = new Stopwatch();
Action useProperty = () =>
{
double res;
for (int counter = 0; counter < 10000000; counter++)
res = Math.Cos(Property);
};
s.Start();
useProperty();
Console.WriteLine("Property Direct : {0}", s.Elapsed);
s.Reset();
Action useMainVar = () =>
{
double res;
for (int counter = 0; counter < 10000000; counter++)
res = Math.Cos(mainVar);
};
s.Start();
useProperty();
Console.WriteLine("Variable from Main: {0}", s.Elapsed);
s.Reset();
Action useLocalVariable = () =>
{
int j = Property;
double res;
for (int counter = 0; counter < 10000000; counter++)
res = Math.Cos(j);
};
s.Start();
useLocalVariable();
Console.WriteLine("Lambda Local : {0}", s.Elapsed);
Console.WriteLine();
}
Console.ReadKey();
}
}
结果: 直接属性:00:00:00.6410370 来自主函数的变量:00:00:00.6265704 Lambda局部变量:00:00:00.2793338
直接属性:00:00:00.6380671 来自主函数的变量:00:00:00.6354271 Lambda局部变量:00:00:00.2798229
直接属性:00:00:00.6337640 来自主函数的变量:00:00:00.6280359 Lambda局部变量:00:00:00.2809130
直接属性:00:00:00.6286821 来自主函数的变量:00:00:00.6254493 Lambda局部变量:00:00:00.2813175
直接属性:00:00:00.6279096 来自主函数的变量:00:00:00.6282695 Lambda局部变量:00:00:00.2783485
大多数较小的属性都会被JIT自动内联,因此对于这些属性来说,性能提升并不明显。
对于非平凡的属性,当缩短查找链时,确实可以节省时间。优化器不能删除冗余调用,因为它无法确定它们是否具有副作用,否则它将改变程序的语义。
有一个例外情况,您不应该将属性值保存到本地:
for (int i=0; i<myArray.Length; i++) { /* do something with myArray[i] */ }
这个模式被JIT识别,它会自动删除数组访问边界测试。
但是如果你这样做:
int len = myArray.Length;
for (int i=0; i<len; i++) { /* do something with myArray[i] */ }
那么边界测试就不能被移除,因为优化器永远无法确定变量 len 是否会被篡改。
所以在“优化”之前三思而后行。
编辑:我并不打算引起有关使用“this”的宗教战争。( 请参见您是否在Java中使用'this'前缀实例变量? ) 在我们公司,我们在字段前使用下划线(这是另一个热门话题); 再加上我们(否则非匈牙利)的命名约定,就可以很明显地看出哪些是属性。使用ReSharper, 我们甚至可以做一些不同颜色编码参数等事情。个人而言,我认为“this”是噪音,并遵循ReSharper的建议将其删除。
最终,我仍然坚持我的答案。虽然我非常重视可读性,但如果您无法快速轻松地确定方法正在执行的操作(使用或不使用“this”),那么它可能太长了。
this
会损害代码的清晰度。 - Rex M我进行了性能计算,虽然很微小,但每个微小的增量都可以在执行代码时节省大量的CPU周期,例如在Web服务器、网络活动等方面同时执行代码数百次。
C#本地变量缓存统计,您也可以下载代码并进行测试。