我在某处读过,拥有公共属性比在一个班级中拥有公共成员更可取。
这只是因为抽象和模块化吗?是否有其他超越原因?
属性访问由编译器转换为函数调用。对于没有备份存储的属性(例如public string UserName { get; set; }
),与直接成员访问相比,性能开销会是多少? (我知道它通常不会有所作为,但在我的一些代码中,属性被访问了数百万次。)
EDIT1: 我在整数成员和属性上运行了一些测试代码,公共成员的速度是属性的3-4倍。 (在调试中~57 ms.vs~206 ms。在Release中57与97 vs. 97是最常见的运行值)。对于1000万次读写,两者都足够小,不足以证明改变任何东西。
代码:
class TestTime1
{
public TestTime1() { }
public int id=0;
}
class TestTime2
{
public TestTime2() { }
[DefaultValue(0)]
public int ID { get; set; }
}
class Program
{
static void Main(string[] args)
{
try
{
TestTime1 time1 = new TestTime1();
TestTime2 time2 = new TestTime2();
Stopwatch watch1 = new Stopwatch();
Stopwatch watch2 = new Stopwatch();
watch2.Start();
for (int i = 0; i < 10000000; i++)
{
time2.ID = i;
i = time2.ID;
}
watch2.Stop();
watch1.Start();
for (int i = 0; i < 10000000; i++)
{
time1.id = i;
i = time1.id;
}
watch1.Stop();
Console.WriteLine("Time for 1 and 2 : {0},{1}",watch1.ElapsedMilliseconds,watch2.ElapsedMilliseconds);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.In.ReadLine();
}
}
答案 0 :(得分:21)
连续运行测试20次,确保在发布版本中启用了JIT优化:
Time for 1 and 2 : 47,66
Time for 1 and 2 : 37,42
Time for 1 and 2 : 25,36
Time for 1 and 2 : 25,25
Time for 1 and 2 : 27,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 26,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
Time for 1 and 2 : 25,25
是的,JITter 擅长内联属性访问器。 Perf不是问题,不应该考虑。
答案 1 :(得分:18)
根本不担心性能开销。它是如此轻微,你不应该考虑削弱类的封装;这将是最糟糕的过早优化。
答案 2 :(得分:9)
这只是因为抽象和模块化吗?还有其他的超越原因吗?
不是我所知道的;这些原因本身就足以引人注目。但也许其他人会加入这个。
属性访问由编译器转换为函数调用。对于没有备份存储的属性(例如,公共字符串UserName {get; set;}),与直接成员访问相比,性能开销会是多少? (我知道它通常不会有所作为,但在我的一些代码中,属性被访问了数百万次。)
在生成的中间语言中,属性访问被转换为方法调用。但是,正如单词所说,这只是一种中间语言:它将及时编译为其他内容。此转换步骤还涉及优化,如内联简单的方法,如简单的属性访问器。
我希望(但你需要测试以确保)JITter负责这样的访问器,所以不应该有性能差异。
答案 3 :(得分:4)
它主要用于抽象(您可以在不破坏现有代码或需要重新编译的情况下添加验证)。
即使使用自动属性,编译器仍然会生成一个支持字段,并且会这样执行。
答案 4 :(得分:3)
确保使用Ctrl-F5而不是F5运行;否则调试器仍将附加,并且某些优化可能无法正常工作,即使在发布模式下也是如此。至少在我的机器上就是这种情况:F5给出与你发布的结果类似的结果,而Ctrl-F5给出了相同的结果。
答案 5 :(得分:1)
1)它用于封装原则,但其他.NET功能使用数据绑定等属性。
2)我不确定我是否同意,我一直听说如果属性是直接获取/设置它的速度与标准字段访问一样快 - 编译器确实如此这适合你。
更新:似乎是两者兼而有之,编译方法调用但JIT优化了。无论哪种方式,这种性能问题都不会对您的代码产生有意义的影响。但是,请注意,围绕实施属性的指导是使它们尽可能轻,呼叫者不希望它们昂贵。
答案 6 :(得分:0)
在我发布this帖后,我发现它基本上是为了隐藏你对象的内部运作。
答案 7 :(得分:0)
之前我曾问过same question。
我猜你正在使用VS2008,正在使用64位操作系统并将编译设置为“任何CPU”?如果是这样,x64 JIT编译器不会内联属性。它们使用32位,使它们在性能上与公共领域相同。
答案 8 :(得分:0)
如果你想要一个特定的例子,你需要使用常规成员变量,那么请考虑继承:如果一个类使用公共成员,那么这个类的派生不能实现验证或其他getter /二传手行为。他们坚持使用变量,如果他们想要做一些不同的事情,他们必须1)忽略现有的成员变量并创建一个新属性,2)添加一个新属性,并且3)覆盖每个方法调用或依赖成员变量来代替使用该属性。这不仅会产生更多不必要的工作,如果编写派生类的人无法访问源代码,那么这几乎是不可能的。
如果基类使用属性而不是成员变量,那只是将验证或其他行为添加到get / set函数的问题,而且你已经完成了。