Подтвердить что ты не робот

Свойства С#, Зачем проверять равенство перед назначением

Почему я вижу, как люди реализуют такие свойства?
Какова точка проверки, соответствует ли значение текущему значению?

public double? Price
{
    get
    {
        return _price;
    }
    set
    {
        if (_price == value)
            return;
        _price = value;
    }
}
4b9b3361

Ответ 1

В этом случае было бы спорным; однако в случае, когда есть связанный побочный эффект (как правило, событие), он избегает тривиальных событий. Например:

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 (возможно, 0, если оно уже 16).

В более сложных примерах могут быть проверки, предварительные изменения и действия после изменения. Все это можно избежать, если вы знаете, что на самом деле это не изменение.

set
{
    if (_price == value)
        return;
    if(value < 0 || value > MaxPrice) throw new ArgumentOutOfRangeException();
    OnPriceChanging();
    _price = value;
    OnPriceChanged();
}

Ответ 2

Это не ответ, а больше: это ответ, основанный на доказательствах, на требование (в другом ответе), что он быстрее проверяет, чем назначать. Короче: нет, это не так. Никакой разницы. Я получаю (для non-nullable int):

AutoProp: 356ms
Field: 356ms
BasicProp: 357ms
CheckedProp: 356ms

(с некоторыми небольшими вариациями на последовательные прогоны - но, по сути, все они берут ровно одно и то же время в любом разумном округлении - при выполнении чего-то 500 МЛН., мы можем игнорировать разницу в 1 мс)

На самом деле, если мы перейдем к int?, получим:

AutoProp: 714ms
Field: 536ms
BasicProp: 714ms
CheckedProp: 2323ms

или double? (как в вопросе):

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

Ответ 3

Предположим, что мы не обрабатываем какие-либо события, связанные с изменением. Я не думаю, что сравнение происходит быстрее, чем размышление. Это зависит от типа данных. Скажем, у вас есть строка, сравнение намного длиннее в худшем случае, чем простое назначение, когда член просто меняет ссылку на ref новой строки. Поэтому я предполагаю, что лучше в этом случае сразу назначить. В случае простых типов данных это не оказывает реального влияния.

Ответ 4

Таким образом, вам не нужно повторно назначать одно и то же значение. Его просто более быстрое выполнение для сравнения значений. AFAIK