class=””>关于.net:C# – 你需要缩短查找链吗?比如在JavaScript中?

C# - do you need to shorten lookup chains, like in JavaScript?

我有一个有公共财产的班级。在一个函数中,我引用这个属性大约30-40次。

1
  this.MyProp;

在函数中定义局部变量会更好吗?,

1
 string myProp = this.MyProp;

这样做之后-在函数中,我缩短了查找链…所以我只需要提到我的道具,而不是这个。

在JavaScript中,缩短查找时间确实提高了性能。在C中会更好/更差吗?因为很明显,我还需要创建另一个局部字符串变量。


从纯性能的角度来看,这取决于属性正在做什么。如果MyPropgetter只返回一个私有字段,那么开销完全是无关紧要的。事实上,除非你明显在做一些重要的事情,比如大量的枚举或者调用数据库,否则这是没有关系的。担心这是微观优化。

但是,需要注意的是属性的用途,即通过封装的例程强制访问值,以确保一致性。如果您试图绕过这一点,您将使代码面临更大的错误风险。

来自@davidandres评论:

根据惯例,属性通常不应该在getter或setter中执行任何实质性的操作。所以我想说,如果您只是通过反射镜或文档或查看自己的代码来验证属性的行为是否良好,那就没有关系了。处理那些明确需要本地化的值的情况应该是一个边缘情况,在验证了它是必要的之后。

编辑:要明确一点,我不建议您出于任何性能原因通常避免使用本地值。正如@guffa所指出的,无论哪种方式,影响都是微不足道的。我指出,属性通常是有原因的属性,默认情况下,对值的访问应该通过属性进行。


不要害怕声明局部变量,它几乎是免费的。在32位系统上,字符串变量将使用四个字节的堆栈空间。总是创建分配局部变量的堆栈帧,因此分配该变量不需要额外的执行时间。

但是,无论您是否应该使用局部变量,都应该基于更正确的情况。除非您有一个循环,在这个循环中,您使用的值是1000倍或更多,否则性能差异甚至很难测量。

(当然,这是基于这样一个假设,即房产得到了适当的实施,因此阅读起来并不昂贵。)


大多数较小的属性都是由JIT自动内联的,因此没有太多的性能增益。

对于非常重要的属性,在缩短查找链时确实可以节省时间。不允许优化器删除多余的调用,因为它无法确定它们是否有副作用,否则它将更改程序的语义。

有一个例外,即不应将属性值保存到本地:

1
for (int i=0; i<myArray.Length; i++) { /* do something with myArray[i] */ }

此模式由JIT识别,它将自动删除数组访问边界测试。

但如果你这样做:

1
2
int len = myArray.Length;
for (int i=0; i<len; i++) { /* do something with myArray[i] */ }

然后无法删除边界测试,因为优化器无法确定是否可以使用变量len进行调节。

所以在你"优化"之前要三思。


对于一个简单的函数和正则属性,这一点都不应该让您担心。不过,对于lambda表达式来说,这似乎是一个很好的问题,快速测试表明,在这种情况下,如果有大量对属性的引用(这与此处所做的类似),那么它可能是值得的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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();
    }
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Property Direct   : 00:00:00.6410370
Variable from Main: 00:00:00.6265704
Lambda Local      : 00:00:00.2793338

Property Direct   : 00:00:00.6380671
Variable from Main: 00:00:00.6354271
Lambda Local      : 00:00:00.2798229

Property Direct   : 00:00:00.6337640
Variable from Main: 00:00:00.6280359
Lambda Local      : 00:00:00.2809130

Property Direct   : 00:00:00.6286821
Variable from Main: 00:00:00.6254493
Lambda Local      : 00:00:00.2813175

Property Direct   : 00:00:00.6279096
Variable from Main: 00:00:00.6282695
Lambda Local      : 00:00:00.2783485

通常不需要"this."来引用同一类中的属性或字段。只有在命名冲突时才使用它。微软的命名准则避免了冲突,所以我不使用它。:)

顺便说一句,如果您有一个方法引用了属性30-40次,那么您很可能有很长的方法味道。

编辑:我不打算开始使用"这个"的宗教战争(参见你在Java中用"这个"来前缀你的实例变量吗?)在我们的公司中,我们在字段前面加上下划线(这是另一个热门话题);再加上我们的(非匈牙利语)命名约定,很明显哪些是属性。使用Resharper,我们甚至可以以不同的方式执行颜色代码参数等操作。我个人认为"这"是噪音,并按照雷斯哈珀的建议来消除它。

最后我坚持我的回答。虽然我高度重视可读性,但是如果您不能快速、轻松地确定一个方法在做什么(这个或不这个),那么它可能太长了。


我做了性能计算,但是它很小,但是每增加一个微小的位都可以在CPU周期上获得巨大的收益,因为您在Web服务器、网络活动等中执行代码100次。

C本地变量缓存统计信息,您还可以下载代码并使用它。