none
Заморочки с операциями == и =, значениями и ссылками RRS feed

  • Общие обсуждения

  • Здравствуйте. Сразу скажу, что понятия не имею, сколько раз похожие темы подымались на всяческих форумах (да и лень искать после работы, честно скажу), но вот мучит один вопрос. Итак...

    Сегодня коллега, который только начинает писать на Java спросил, как в ней сравниваются строки. Я, без доли сомнений, на полном автомате ответил "операцией ==". М-да, подумалось потом... Оно не сработает же, ведь это будет сравнение ссылок на объекты, и потому всегда результат будет отрицательным (false в данном случае), если строки разные (или какие там ещё ньюансы есть в JRE/JDK). А посему нужно использовать более явный String.equals(String). Вместо того, чтобы продолжать работать над своим проектом, я немного погрузился в размышления по поводу того, что в C# операция "==" в System.String переопределена и таким образом не позволяет сравнивать ссылки, что не совсем явно с первого взгляда на язык, хоть и вполне интуитивно.

    Я, конечно, понимаю, что в переопределении операций есть и плюсы и минусы, но... Мне кажется, что чистота кода, и сокращение к минимуму неявного поведения языка — лучшая практика. Вряд ли кто-то будет переопределять "operator ==", даже если это может предлагаться компилятором, но если кто-то умудрится это сделать без проса (а это часто бывает в разных ситуациях), то погоня за багами в таком случае просто неизбежна. Да и языку не красиво вот так себя вести.

    Вот давайте представим: операции "==" для сравнения ссылок на объекты в C# не существует, как и его "близнеца", операции "!=". Пожалуй, тогда ещё и для System.Object не должно существовать операции "==" для сравнения значений, но здесь я в рассуждении не силён. И, также давайте представим, что для сравнения ссылок у нас есть какой-нибудь оператор. Пусть это будет тот же is (как в VB6), но только с правым операндом не в виде имени класса, а в виде имени объекта — компилятору просто это определить статически. Т.е., например, такой вот код был бы вполне рабочим:

    if ( o1 is null ) { ... }

    Как по мне, здесь чётко ясно, что здесь происходит явная "проверка объекта на жизнь". "o1 != null", может, можно было бы заменить специальным синтаксисом типа "o1 !is null" ("!o2" нельзя, поскольку операция "!" в данном случая применима к "o2"). А вот чтобы сравнить значение объектов классов или структур — можно было было использовать переопределение "operator ==". В развитии этой мысли вполне логичным было бы предположить, что и для присвоения ссылок на объекты классов (но не структур) нужно использовать также явный синтаксис. Например:

    set c1 = new C1();

    Впрочем, мне сейчас трудно предложить синтаксис определения локальных объектов, когда нужно ещё и тип объектов указать, но, по всей видимости, его можно в таком случае было бы оставить без изменений. А, может, и нет. Но пока это не важно. Кстати set, конечно же мог бы возвращать значения, но его нельзя бы было переопределить, что дало бы возможность следующей записи:

    if ( (set c1 = new C1()) !is c2 ) { ... }

    Увидев такой код, я бы вряд ли стал бы задумываться, как иногда это у меня бывает при просмотре нового кода на C#. Впрочем, вот и всё. Каково ваше мнение по этому поводу? В каких языках используют подобную практику, кроме VB6? Смысл в сказанном мною есть?

    P.S. Подозреваю, такое есть в VB.NET, но я с ним вообще не знаком.
    • Изменено Цыба 5 ноября 2009 г. 23:04 удалил не несущее смысл предложение
    • Перемещено Siddharth Chavan 1 октября 2010 г. 22:03 MSDN Forums Consolidation (От:Visual C#)
    5 ноября 2009 г. 22:57

Все ответы

  • Мне кажется, не стоит перегружать язык лишними ключевыми словами и операторами. В данном случае оператора == достаточно для сравнения самих объектов, если он перегружен, или же сравнения ссылок, если нет.

    Например код

    if (o1 == null) {...}

    мало чем отличается от

    if (o1 is null) {...} // Если брать "новый" оператор

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


    Также замечу, что присвоение ссылок с отдельным ключевым словом set - совсем бесполезная идея, т.к. это просто утяжелит код: всё равно ссылочной переменной ничего кроме ссылки и null присвоить нельзя.
    6 ноября 2009 г. 7:20
  • Спасибо за ответ, ZackFlame.

    >> Мне кажется, не стоит перегружать язык лишними ключевыми словами и операторами.
    >> В данном случае оператора == достаточно для сравнения самих объектов, если он
    >> перегружен, или же сравнения ссылок, если нет.

    Я, наоборот, имел в виду, что сравнение ссылок на объекты классов является тривиальной задачей, и замещать её переопределённым оператором ==, в принципе, не имеет смысла.

    >> if (o1 == null) {...}
    >> мало чем отличается от
    >>if (o1 is null) {...} // Если брать "новый" оператор

    Не совсем соглашусь. При просмотре нового кода у меня часто возникает вполне естественное желание быть уверенным в том, что здесь происходит - сравнение ссылок (если указан объект вместо null), или сравнение значений. И даже если операция сравнения в классе будет замещена, я буду вполне уверен в происходящем.

    >> поэтому в нём нет необходимости. Если же нужно явно проверить равенство ссылок,
    >> то есть статический метод Object.ReferenceEquals(Object objA, Object objB)

    Операция is была бы куда элегантней.

    >> Также замечу, что присвоение ссылок с отдельным ключевым словом set - совсем
    >> бесполезная идея, т.к. это просто утяжелит код: всё равно ссылочной переменной
    >> ничего кроме ссылки и null присвоить нельзя.

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


    (не комментарий)
    Одним словом, мне не нравится, что одни и те же операции используются для разных за смыслом действий.
    6 ноября 2009 г. 8:44
  • Один и тот же оператор == используется для одного по смыслу действия, а именно сравнением двух сущностей.(если вы конечно не загнали в него расчет движения планет солнечной системы)
    А вот что это за сущности вопрос другой.
    is используется для куда более подходящего действия - соответствие типу.
    6 ноября 2009 г. 10:06
  • Один и тот же оператор == используется для одного по смыслу действия, а именно сравнением двух сущностей.(если вы конечно не загнали в него расчет движения планет солнечной системы)
    Это когда в C можно было указатели сравнивать без предупреждений с целыми, хоть адрес какой-либо переменной вряд лы имел ещё какое-либо значение. Смысл разный.
    is используется для куда более подходящего действия - соответствие типу.
    Конечно, возвращая только true/false. Мог бы и для соответствия ссылок друг другу возвращать true/false.
    6 ноября 2009 г. 10:08
  • Повторюсь, оператор == обозначает действие, а не объекты, ему всё равно что сравнивать, в конце концов это только алиас для функции op_Equality.
    6 ноября 2009 г. 10:22