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; }
    }
}