пятница, 21 февраля 2014 г.

Про System.Drawing.Color и оператор ==

Блог переехал. Актуальная версия поста находится по адресу: http://aakinshin.net/ru/blog/dotnet/system-drawing-color-equals/.


Для многих стандартных структур в .NET-е переопределён оператор ==, который позволяет легко сравнивать ваши объекты. К сожалению, далеко не все задумываются о том, что на самом деле сравнивается при работе с этим замечательным оператором. В этой короткой заметке мы посмотрим логику сравнения объектов на примере System.Drawing.Color. Как вы думаете, что выведет следующий код:

var redName = Color.Red;
var redArgb = Color.FromArgb(255, 255, 0, 0);    
Console.WriteLine(redName == redArgb);    

«И тут красный, и там красный. Наверное, объекты должны быть равны.», — подумает читатель. Но давайте откроем исходный код и посмотрим на оператор ==:

public static bool operator ==(Color left, Color right) {
    if (left.value == right.value
        && left.state == right.state
        && left.knownColor == right.knownColor) {

        if (left.name == right.name) {
            return true;
        }

        if (left.name == (object) null || right.name == (object) null) {
            return false;
        }

        return left.name.Equals(right.name);
    }

    return false;
}

Изучение исходного кода подталкивает нас к интересному выводу: цвета сравниваются не по ARGB-значанию, а по свойству Name. Какое же имя у наших объектов? Давайте посмотрим:

Console.WriteLine(redName.Name); // Red
Console.WriteLine(redArgb.Name); // ffff0000

Хм, имена-то разные. Таким образом, выражение redName == redArgb вернёт нам False. Неприятная ситуация может получится, если, например, исходный Color.Red был сериализован в ARGB, затем десериализрован обратно, после чего вы вздумали сравнить итоговый цвет с оригиналом. Давайте почитаем, что пишут про оператор == в MSDN:

This method compares more than the ARGB values of the Color structures. It also does a comparison of some state flags. If you want to compare just the ARGB values of two Color structures, compare them using the ToArgb method.

Ну, теперь всё понятно, для сравнения ARGB-значений наших цветов нам нужен метод ToArgb:

Console.WriteLine(redName.ToArgb() == redArgb.ToArgb()); // True

Я думаю, не следует полагаться на догадки о логике работы стандартных методов сравнения, которые изначально могут показаться вам очевидными. Если вы пользуетесь оператором == или методом Equals для значимых типов, то неплохо было бы сначала заглянуть в документацию и проверить, что именно будет сравниваться.

Комментариев нет:

Отправить комментарий