C#属性,为什么在赋值之前检查相等
为什么我看到人们实现这样的属性C#属性,为什么在赋值之前检查相等,c#,properties,C#,Properties,为什么我看到人们实现这样的属性 检查该值是否等于当前值有什么意义 public double? Price { get { return _price; } set { if (_price == value) return; _price = value; } } 这样,您就不必重新分配相同的值。比较值的执行速度更快。AFAIK在这种情况下,它将是没有意义的;但是,在存在相
检查该值是否等于当前值有什么意义
public double? Price
{
get
{
return _price;
}
set
{
if (_price == value)
return;
_price = value;
}
}
这样,您就不必重新分配相同的值。比较值的执行速度更快。AFAIK在这种情况下,它将是没有意义的;但是,在存在相关副作用(通常是事件)的情况下,它可以避免琐碎的事件。例如:
set
{
if (_price == value)
return;
_price = value;
OnPriceChanged(); // invokes the Price event
}
现在,如果我们这样做:
foo.Price = 16;
foo.Price = 16;
foo.Price = 16;
foo.Price = 16;
我们没有得到4个事件;我们最多得到1(如果已经是16,可能是0)
在更复杂的示例中,可能有验证、变更前操作和变更后操作。如果你知道这实际上不是一个改变,那么所有这些都可以避免
set
{
if (_price == value)
return;
if(value < 0 || value > MaxPrice) throw new ArgumentOutOfRangeException();
OnPriceChanging();
_price = value;
OnPriceChanged();
}
set
{
如果(_price==值)
返回;
如果(value<0 | | value>MaxPrice)抛出新的ArgumentOutOfRangeException();
OnPriceChanging();
_价格=价值;
OnPriceChanged();
}
这不是一个答案,更重要的是:这是一个基于证据的回应(在另一个答案中),即检查比分配更快。简言之:不,不是。没什么区别。我得到(对于不可为空的int
):
(连续运行时会有一些小的变化,但在任何合理的舍入范围内,它们都会占用完全相同的时间,当执行5亿次操作时,我们可以忽略1ms的差异)
事实上,如果我们改为int?
我会得到:
AutoProp: 714ms
Field: 536ms
BasicProp: 714ms
CheckedProp: 2323ms
或double?
(如问题中所示):
因此,这不是性能助手
通过测试
class Test
{
static void Main()
{
var obj = new Test();
Stopwatch watch;
const int LOOP = 500000000;
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.AutoProp = 17;
}
watch.Stop();
Console.WriteLine("AutoProp: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.Field = 17;
}
watch.Stop();
Console.WriteLine("Field: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.BasicProp = 17;
}
watch.Stop();
Console.WriteLine("BasicProp: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.CheckedProp = 17;
}
watch.Stop();
Console.WriteLine("CheckedProp: {0}ms", watch.ElapsedMilliseconds);
Console.ReadLine();
}
public int AutoProp { get; set; }
public int Field;
private int basicProp;
public int BasicProp
{
get { return basicProp; }
set { basicProp = value; }
}
private int checkedProp;
public int CheckedProp
{
get { return checkedProp; }
set { if (value != checkedProp) checkedProp = value; }
}
}
类测试
{
静态void Main()
{
var obj=新测试();
秒表;
循环常数int=500000000;
watch=Stopwatch.StartNew();
for(int i=0;i
假设我们不处理任何与更改相关的事件。
我不认为比较比学习快。这取决于数据类型。假设您有一个字符串,在最坏的情况下,比较要比简单赋值长得多,在简单赋值中,成员只需更改对新字符串的ref的引用。
所以我想,在这种情况下,最好马上分配。
对于简单的数据类型,它不会产生真正的影响。[需要引证]lol-R#说“分配前冗余检查”,我的resharper没有抱怨……啊,我写的是
if(field!=value)field=value
-注意,它不会影响计时,但实际上,R#不会在if(field==value)返回时发现它;字段=值代码>接近比较比分配快。比如说,如果它是一个大数据结构,并且它的内存必须一次又一次地创建(ref类型),或者如果它不是第一次被实例化,那么就会抛出异常。如果这是真的,这是否意味着我们应该避免使用auto属性?我在这个声明中没有看到任何证据。请看我为演示添加的测试。抱歉,伙计们,我的错误。感谢您带走我的汗水并获得积分;)是的,我在想,如果它是直接分配的话,那是没有意义的。一个使用秒表计时的纯分配快速测试显示,支票实际上使它慢了一倍,但仍然是非常少的时间。即使有一百万次迭代,所花费的时间也只有0.05秒。我这样做的唯一地方就是马克所建议的。如果值发生变化,我想触发一个事件(或执行一些其他附加处理),但如果它保持不变,则不触发。很抱歉,我在比较vs assignment的性能方面犯了错误。但同样,你没有直接回答问题。我的意思是,是的,如果价值观不同,那么在事件中所说的话是正确的。但是以OP的核心代码为例,您将如何解释为什么要这样做?@zenwalker IMO,这样做是出于习惯,或者是因为查看生成的代码(即在.designer.cs中)并看到模式但缺少上下文。如前所述,它不会增加任何价值(但会增加开销)。所以本质上,我对这个问题的回答只是我回答的前7个字:“在这种情况下,它将是没有意义的。”。奇怪。我比较了BasicProp和CheckedProp,它为CheckedProp提供了更长的时间。只是出于好奇,你在运行64位系统吗?@markoo是的,win-7,x64,i7cpu。顺便说一句,我为一些不同的数据类型添加了计时,就是这样。我首先测试的可空值。有趣。@markoo是的,“提升的操作员”的开销更大一些;在这种情况下更值得注意,因为int
和double
具有直接的IL相等实现;在像DateTime
或decimal
这样的情况下,它就不那么明显了。很高兴知道。另请注意:您的支票也比检查==然后返回要快得多。
AutoProp: 535ms
Field: 535ms
BasicProp: 539ms
CheckedProp: 3035ms
class Test
{
static void Main()
{
var obj = new Test();
Stopwatch watch;
const int LOOP = 500000000;
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.AutoProp = 17;
}
watch.Stop();
Console.WriteLine("AutoProp: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.Field = 17;
}
watch.Stop();
Console.WriteLine("Field: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.BasicProp = 17;
}
watch.Stop();
Console.WriteLine("BasicProp: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
obj.CheckedProp = 17;
}
watch.Stop();
Console.WriteLine("CheckedProp: {0}ms", watch.ElapsedMilliseconds);
Console.ReadLine();
}
public int AutoProp { get; set; }
public int Field;
private int basicProp;
public int BasicProp
{
get { return basicProp; }
set { basicProp = value; }
}
private int checkedProp;
public int CheckedProp
{
get { return checkedProp; }
set { if (value != checkedProp) checkedProp = value; }
}
}