Свойства С#, Зачем проверять равенство перед назначением
Почему я вижу, как люди реализуют такие свойства?
Какова точка проверки, соответствует ли значение текущему значению?
public double? Price
{
get
{
return _price;
}
set
{
if (_price == value)
return;
_price = value;
}
}
Ответы
Ответ 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