在下一种情况下声明新变量是否有任何性能成本:
这是一个例子,只是为了证明这一点.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我有下一个方法:
选项1:
public void MyMethod(Person person)
{
if (person.FirstName.Contains("Ro") || (person.LastName.StartsWith("A") && person.Age > 20))
{
//Do something
}
else if (person.FirstName.Contains("Ko") || (person.LastName.StartsWith("B") && person.Age >= 40))
{
//Do something
}
else if (person.FirstName.Contains("Mo") || (person.LastName.StartsWith("C") && person.Age > 60))
{
//Do something
}
else
{
//Do something
}
}
Run Code Online (Sandbox Code Playgroud)
选项2:
public void MyMethod(Person person)
{
string firstName = person.FirstName;
string lastName = person.LastName;
int age = person.Age;
if (firstName.Contains("Ro") || (lastName.StartsWith("A") && age > 20))
{
//Do something
}
else if (firstName.Contains("Ko") || (lastName.StartsWith("B") && age >= 40))
{
//Do something
}
else if (firstName.Contains("Mo") || (lastName.StartsWith("C") && age > 60))
{
//Do something
}
else
{
//Do something
}
}
Run Code Online (Sandbox Code Playgroud)
同样,这只是一个证明问题概念的例子.
问题:选项1和选项2之间是否存在任何性能或内存问题?
可以肯定的是,选项2看起来更好,更具可读性.
这是由抖动解决的,它积极地消除了方法的局部变量,并寻找将它们存储在CPU寄存器中的方法.无论你是否自己声明变量,它都会这样做.你的属性getter很简单,没有副作用,抖动可以自己找到.这些getter方法也被删除,它们被内联.实际上,它会将您的代码从第一个代码段转换为第二个代码段.
这是您无法通过Reflection找出方法所具有的局部变量的核心原因.为什么调试优化代码时遇到问题.以及为什么volatile关键字存在于C#中.当抖动优化器完成后,局部变量就不再存在了.
您将在本答案中找到抖动执行的优化类型的概述.
所以,不,不要犹豫,以这种方式使您的代码更具可读性,预计不会产生任何影响.但请记住,您可以在代码中引入一个错误,当代码的其余部分影响您正在使用的对象时,它将触发.你当然会存储该物业的陈旧价值.如果类是非平凡的并且属性getter本身具有副作用,那么这并不总是那么明显.否则,.NET中的编码指南要求具有副作用的属性应该是方法的核心原因.
嗯,我发现选项1更具可读性.选项2意味着您正在操纵这些属性的值.解析代码需要一两秒钟才能弄清楚你不是.
所以这只是一种风格选择.请参阅您的样式指南,了解您的组织或特定代码库所偏好的内容.
性能差异将是零.你可能不会相信我,如果没有,唯一可以确定的方法是为自己实际测试它.但这真的是浪费时间.
属性getter应该(除非文档另有说明)具有常量时间执行,因此查询属性的值应该与查询局部变量的值没有什么不同.因此,可能影响性能的唯一另一件事是,使用选项2 ,当代码可能永远不会到达需要姓氏或年龄的分支时,您最终可能会查询所有属性的值.但这可能会变化很大.您实际上只能通过使用一些真实数据重复进行基准测试来确定这一点.除非这种方法确实被证明是瓶颈,否则不要浪费你的时间.它不会.
如果您打算在可读性之外的某些基础上做出决定,那么它将是线程安全的.如果属性getter返回的值可能会在另一个线程修改对象后发生更改,那么使用选项1时可能会出现问题.这会使得将这些属性的值缓存到局部变量中更为理想,因此您可以选择选项2 .
但这正是编译器不会进行任何类型的缓存,将选项1转换为选项2的原因,正如其他一些答案所暗示的那样.生成的代码会有所不同,性能差异不会很大.(虽然JITter肯定可以在运行时执行这种类型的优化,正如Hans在他的回答中指出的那样.)