Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования. Нашёл интересную статью по этому поводу (правда там Ява, но ничего это не меняет). Там высказывается вполне корректная на мой взгляд точка зрения: equals не должен быть методом объекта, иначе он будет не симметричным, что ведёт к логическим ошибкам.
Здравствуйте, Курилка, Вы писали:
К>Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования. Нашёл интересную статью по этому поводу (правда там Ява, но ничего это не меняет). Там высказывается вполне корректная на мой взгляд точка зрения: equals не должен быть методом объекта, иначе он будет не симметричным, что ведёт к логическим ошибкам.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
К>>Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования. Нашёл интересную статью по этому поводу (правда там Ява, но ничего это не меняет). Там высказывается вполне корректная на мой взгляд точка зрения: equals не должен быть методом объекта, иначе он будет не симметричным, что ведёт к логическим ошибкам.
ZS>Ну не зря же этот метод виртуальный?
Если подходить с т.зр. логики — зря.
Практически в угоду ООП извратили суть отношения равенства, а ёжики "плевались, но лезли на кактус"
Правда с практической т.зр. проблема не такая уж страшная.
...
ZS>>Ну не зря же этот метод виртуальный?
К>Если подходить с т.зр. логики — зря. К>Практически в угоду ООП извратили суть отношения равенства, а ёжики "плевались, но лезли на кактус" К>Правда с практической т.зр. проблема не такая уж страшная.
Использование классовых методов, реализованных через экземплярные спасет мир.
class Object
...
public static bool Equals(object objA, object objB)
{
if (objA == objB)
{
return true;
}
if ((objA != null) && (objB != null))
{
return objA.Equals(objB);
}
return false;
}
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
ZS>...
ZS>>>Ну не зря же этот метод виртуальный?
К>>Если подходить с т.зр. логики — зря. К>>Практически в угоду ООП извратили суть отношения равенства, а ёжики "плевались, но лезли на кактус" К>>Правда с практической т.зр. проблема не такая уж страшная.
ZS>Использование классовых методов, реализованных через экземплярные спасет мир.
Классовые — это статические типа чтоль?
Чем твоя схема отличается от objA.equals(objB), которая несимметрична?
Доп. проверки на равенство экземпляров и null роли тут не играют
..
ZS>>Использование классовых методов, реализованных через экземплярные спасет мир.
К>Классовые — это статические типа чтоль? К>Чем твоя схема отличается от objA.equals(objB), которая несимметрична? К>Доп. проверки на равенство экземпляров и null роли тут не играют
Ну, я обычно сравниваю объекты на равенство статическим методом базового класса. И мне не важен тип объекта, часто я его и не знаю. По-моему, удобно. Пусть и извращение.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
ZS>..
ZS>>>Использование классовых методов, реализованных через экземплярные спасет мир.
К>>Классовые — это статические типа чтоль? К>>Чем твоя схема отличается от objA.equals(objB), которая несимметрична? К>>Доп. проверки на равенство экземпляров и null роли тут не играют
ZS>Ну, я обычно сравниваю объекты на равенство статическим методом базового класса. И мне не важен тип объекта, часто я его и не знаю. По-моему, удобно. Пусть и извращение.
Ну почему извращение, это как раз по сути логически более правильный способ.
Просто проблема с перегрузкой оператора, которая-то и делает его несимметричным. Если же один метод в базовом классе, который ещё и final, то это почти идентично выносу его из класса.
..
ZS>>Ну, я обычно сравниваю объекты на равенство статическим методом базового класса. И мне не важен тип объекта, часто я его и не знаю. По-моему, удобно. Пусть и извращение.
К>Ну почему извращение, это как раз по сути логически более правильный способ.
Вот и получается, что виртуальный метод Equal необходим.
К>Просто проблема с перегрузкой оператора, которая-то и делает его несимметричным. Если же один метод в базовом классе, который ещё и final, то это почти идентично выносу его из класса.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
ZS>..
ZS>>>Ну, я обычно сравниваю объекты на равенство статическим методом базового класса. И мне не важен тип объекта, часто я его и не знаю. По-моему, удобно. Пусть и извращение.
К>>Ну почему извращение, это как раз по сути логически более правильный способ.
ZS>Вот и получается, что виртуальный метод Equal необходим.
Откуда получается-то?
Проблема в том, что это в базовый класс запёхано, поэтому приходиться находить обходные пути.
Скажи, зачем тебе реально тут виртуальность нужна?
ObjectA.Equals(a,b) даже визуально гораздо "чище", чем a.equals(b)
Здравствуйте, Курилка, Вы писали:
К>Здравствуйте, ZevS, Вы писали:
ZS>>Здравствуйте, Курилка, Вы писали:
ZS>>..
ZS>>>>Ну, я обычно сравниваю объекты на равенство статическим методом базового класса. И мне не важен тип объекта, часто я его и не знаю. По-моему, удобно. Пусть и извращение.
К>>>Ну почему извращение, это как раз по сути логически более правильный способ.
ZS>>Вот и получается, что виртуальный метод Equal необходим.
К>Откуда получается-то? К>Проблема в том, что это в базовый класс запёхано, поэтому приходиться находить обходные пути. К>Скажи, зачем тебе реально тут виртуальность нужна? К>ObjectA.Equals(a,b) даже визуально гораздо "чище", чем a.equals(b)
А если a и b на самом деле являются экземплярами наследника ObjectA?
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
К>>Проблема в том, что это в базовый класс запёхано, поэтому приходиться находить обходные пути. К>>Скажи, зачем тебе реально тут виртуальность нужна? К>>ObjectA.Equals(a,b) даже визуально гораздо "чище", чем a.equals(b)
ZS>А если a и b на самом деле являются экземплярами наследника ObjectA?
Если
Ну а даже и если, и если один ObjectA1, другой ObjectA2, плюс в обоих метод переопределён, получаем веселуху
Здравствуйте, Курилка, Вы писали:
К>Здравствуйте, ZevS, Вы писали:
ZS>>Здравствуйте, Курилка, Вы писали:
К>>>Проблема в том, что это в базовый класс запёхано, поэтому приходиться находить обходные пути. К>>>Скажи, зачем тебе реально тут виртуальность нужна? К>>>ObjectA.Equals(a,b) даже визуально гораздо "чище", чем a.equals(b)
ZS>>А если a и b на самом деле являются экземплярами наследника ObjectA?
К>Если К>Ну а даже и если, и если один ObjectA1, другой ObjectA2, плюс в обоих метод переопределён, получаем веселуху
Ну это — по желанию. Можно и статический метод идиотски закодировать.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
К>>Здравствуйте, ZevS, Вы писали:
ZS>>>Здравствуйте, Курилка, Вы писали:
К>>>>Проблема в том, что это в базовый класс запёхано, поэтому приходиться находить обходные пути. К>>>>Скажи, зачем тебе реально тут виртуальность нужна? К>>>>ObjectA.Equals(a,b) даже визуально гораздо "чище", чем a.equals(b)
ZS>>>А если a и b на самом деле являются экземплярами наследника ObjectA?
К>>Если К>>Ну а даже и если, и если один ObjectA1, другой ObjectA2, плюс в обоих метод переопределён, получаем веселуху
ZS>Ну это — по желанию. Можно и статический метод идиотски закодировать.
Ну ты серьёзно чтоль не улавливаешь разницу? Или тебе надо рассказывать, что статический определён на этапе компиляции, а виртуальный нет?
...
ZS>>Ну это — по желанию. Можно и статический метод идиотски закодировать.
К>Ну ты серьёзно чтоль не улавливаешь разницу? Или тебе надо рассказывать, что статический определён на этапе компиляции, а виртуальный нет?
А собственно Equal то тут при чем?
То что виртульные мотоды определяются через vtm — фича.
Здравствуйте, Курилка, Вы писали:
К>Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования...
Кое-что по теме здесь и здесь.
Здравствуйте, ZevS, Вы писали:
ZS>Здравствуйте, Курилка, Вы писали:
ZS>...
ZS>>>Ну это — по желанию. Можно и статический метод идиотски закодировать.
К>>Ну ты серьёзно чтоль не улавливаешь разницу? Или тебе надо рассказывать, что статический определён на этапе компиляции, а виртуальный нет?
ZS>А собственно Equal то тут при чем?
блин, я думал ты поймёшь, что он статический ZS>То что виртульные мотоды определяются через vtm — фича.
попробуй понять статью, надоело уже повторять, если честно
Здравствуйте, lxa, Вы писали:
lxa>Здравствуйте, Курилка, Вы писали:
К>>Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования... lxa>Кое-что по теме здесь и здесь.
Сильно не вчитывался, как говорят "много букав"
Только вот всё это похоже на шаманские танцы вокруг грабель.
Вынос в статический метод сделал бы ситуацию много понятней и проще.
Здравствуйте, Курилка, Вы писали:
К>Тут как-то пару месяцев обсуждали этот метод у Object из .Net в рамках наследования. Нашёл интересную статью по этому поводу (правда там Ява, но ничего это не меняет). Там высказывается вполне корректная на мой взгляд точка зрения: equals не должен быть методом объекта, иначе он будет не симметричным, что ведёт к логическим ошибкам.
Откуда логическая ошибка? Методы вызываются у целевого объекта, соотв. он служит образцом, запутаться невозможно. Понятное дело, что сравнение может быть нессиметричным, если аргумент является подтипом целевого, т.е. может содержать больше аттрибутов.
А во-вторых, у тебе есть требуемая возможность — определяй себе на здоровье static bool operator==(T a, T b) {...}
-----
А фатанатичное стремление соблюдать LSP вообще не понимаю. Согласен лишь с тем, что несоблюдение принципа может привести к популярным ошибкам проетиктирования (наиболее популярная — неоправданное увеличение связанности и последующая трудность независимого развития частей системы), однако его строгое соблюдение так же может спровоцировать лишние телодвижения на ровном месте. ИМХО, зачастую принадлежность некоей классификации удобней устанавливать напрямую через аттрибуты состояния (виртуальные), чем через сетку двойных диспечеризаций и прочих "посетителей".
Простой пример, предположим есть поток, у потока есть метод "Seek(args)", и парный аттрибут "bool CanSeek". Яркий пример нарушения LSP, тем не менее крайне удобный для использования, позволяющий одним if-ом выбрать наиболее подходящий способ работы с потоком.
Так что если нарушение LSP в конкретных случаях не добавляет связанности, но упрощает представление алгоритмов, то почему бы и нет? В обсуждаемом Equals(arg) мы обычно приводим к тому типу, коим являемся, т.е. связанность увеличиться никак не может, наоборот, может резко снизиться, за счёт использования методов уровня базового фреймворка.
Здравствуйте, vdimas, Вы писали:
V>Простой пример, предположим есть поток, у потока есть метод "Seek(args)", и парный аттрибут "bool CanSeek". Яркий пример нарушения LSP, тем не менее крайне удобный для использования, позволяющий одним if-ом выбрать наиболее подходящий способ работы с потоком.
Здравствуйте, Курилка, Вы писали:
К>Практически в угоду ООП извратили суть отношения равенства, а ёжики "плевались, но лезли на кактус" К>Правда с практической т.зр. проблема не такая уж страшная.
IMHO в угоду ООП извратили большую часть решаемых программистами-ёжиками задач, а это с практической т.зр. проблема может и не страшная, но весьма противная.
Здравствуйте, igna, Вы писали:
I>IMHO в угоду ООП извратили большую часть решаемых программистами-ёжиками задач, а это с практической т.зр. проблема может и не страшная, но весьма противная.
"извратили большую часть решаемых ... задач" — это как?
V>>Простой пример, предположим есть поток, у потока есть метод "Seek(args)", и парный аттрибут "bool CanSeek". Яркий пример нарушения LSP, тем не менее крайне удобный для использования, позволяющий одним if-ом выбрать наиболее подходящий способ работы с потоком.
EC>А где он тут нарушается?
Тут сразу несколько нарушений.
1. Интерфейс класса содержит метод, который заведомо может быть не реализован. По-хорошему таких методов быть не должно, их нужно перенести в некий наследник StreamEx.
2. Собсно запрос CanSeek по смыслу означает запрос is_a(StreamEx) , получи нарушение.
В публичных интерфейсах классов и интерфейсов дотнета полно таких "сладких парочек".
Здравствуйте, igna, Вы писали:
I>Здравствуйте, ZevS, Вы писали:
ZS>>Есть различные подходы к их решению. ООП — один из них, и выбор или не выбор его диктуется (в идеале) задачей.
I>Да, но зачем Java и C# заставляют использовать статические функции вместо обыкновенных в стиле С?
Наверное, потому что C — не ОО язык, а шарп и жаба — да.
ps: лично меня никто не заставляет использовать статические функции. + необходимость в статической функцией не связаной логически с классом возникает крайне редко.
Здравствуйте, ZevS, Вы писали:
ZS>ps: лично меня никто не заставляет использовать статические функции. + необходимость в статической функцией не связаной логически с классом возникает крайне редко.
Здравствуйте, igna, Вы писали:
I>Здравствуйте, ZevS, Вы писали:
ZS>>ps: лично меня никто не заставляет использовать статические функции. + необходимость в статической функцией не связаной логически с классом возникает крайне редко.
I>А здесь
Зависит от задачи. Возможно ни один из перечисленных: как пример, может потребоваться абстрактная фабрика. Если тебе не нравится ООП — используй указатели на функции. Как по мне, ОО вариант приятнее...
Здравствуйте, WolfHound, Вы писали:
WH>GetCreationTime чего? Из использования совсем не ясно. А вот File.GetCreationTime сразу понятно.
Так напиши GetFileCreationTime, заодно и с грамматикой все будет в порядке. Кроме того имеешь выбор, в одних случаях подойдет имя подлинее, в других — покороче.
I>Так напиши GetFileCreationTime, заодно и с грамматикой все будет в порядке. Кроме того имеешь выбор, в одних случаях подойдет имя подлинее, в других — покороче.
Даже FileGetCreationTime или File_GetCreationTime можешь написать, и против синтаксиса File.GetCreationTime я ничего против не имею. Но C# и Java не позволяют просто GetCreationTime.
Здравствуйте, igna, Вы писали:
I>Даже FileGetCreationTime или File_GetCreationTime можешь написать, и против синтаксиса File.GetCreationTime я ничего против не имею. Но C# и Java не позволяют просто GetCreationTime.
Пространства имен. Не нужно объяснять какая у них польза?
Статические методы можно рассматривать как вариант метода в пространтве имен
Здравствуйте, igna, Вы писали:
I>Здравствуйте, AndrewVK, Вы писали:
AVK>>В .NET есть два разных подхода: I>
AVK>>DateTime dt1 = File.GetCreationTime("myfile.txt");
AVK>>DateTime dt2 = new FileInfo("myfile.txt").CreationTime;
I>
I>Я бы все же предпочел
I>
I>DateTime dt = GetCreationTime("myfile.txt");
I>
Всё зависит от того, что имеется в виду. У FileInfo может быть произвольное количество конструкторов, которые позволяют уточнить всякие дополнительные параметры. Удобнее иметь 5 конструкторов объекта, у которого можно потом позвать GetCreationTime, GetLastAccessTime и так далее, чем иметь по 5 перегрузок на каждый метод.
А между
File.GetCreationTime("myfile.txt");
и
GetFileCreationTime("myfile.txt");
я принципиальной разницы не вижу.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Курилка, Вы писали:
К>+ зачем мне отдельный класс и инстанс компарера?
Как зачем? Для того, чтобы иметь возможность иметь разные компараторы. Разные инстансы могут иметь разное состояние, которое определяет их поведение.
Да, всё то же самое можно сэмулировать при помощи чистых функций, но ничего особенно интересного от этого не произойдет. Это будет всего лишь другой взгляд на те же самые вещи. В дотнете вызов делегата все еще дороже, чем вызов метода интерфейса, поэтому данный выбор оправдан технически.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Курилка, Вы писали: К>Вот и получаем, что "упираемся" по сути в .Net и ООП идеологию. На первый вопрос у тебя нет ответа?
Какой? Про неймспейс? Это недоработка языка. В следующем шарпе ея поправят. Будешь себе писать apples.Sort(appleComparer) и в ус не дуть. А то и вовсе apples.SortBy(apple => apple.Color);
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Какой? Про неймспейс? Это недоработка языка. В следующем шарпе ея поправят. Будешь себе писать apples.Sort(appleComparer) и в ус не дуть. А то и вовсе apples.SortBy(apple => apple.Color);
Сенкс, правда на шарпе писать не уверен, что буду.
Плюс довольно странная с логической т.зр. сортировка (по меньшей мере для меня) — вот ты с ходу ответишь какой цвет больше пурпурный или оранжевый?
Т.е. не вижу как построить для цветов отношение порядка, чтоб оно было понятным человеку и непротиворечивым.
Здравствуйте, Курилка, Вы писали:
К>Сенкс, правда на шарпе писать не уверен, что буду.
Никто и не навязывает. К>Плюс довольно странная с логической т.зр. сортировка (по меньшей мере для меня) — вот ты с ходу ответишь какой цвет больше пурпурный или оранжевый?
Это неважно. Не я придумал сортировать яблоки — не мне и критерий изобретать. Может, там червивость роль играет.
Главная идея OrderBy().ThenBy() — в том, что вместо функции, отображающей пару объектов на {-1, 0, 1}, используется функция, отображающая объект в ключ сортировки (предполагается, что для ключа компаратор уже существует). Иногда проще написать одно, иногда — другое.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Ну эт всё ясно.
У меня вот к этому был вопрос/претензия:
К>+ зачем мне отдельный класс и инстанс компарера?
Как зачем? Для того, чтобы иметь возможность иметь разные компараторы. Разные инстансы могут иметь разное состояние, которое определяет их поведение.
Откуда в компараторе состояние? Если этим ты описываешь какие-то параметры сортировки, то ещё понятно.
Но это состоянием не будет, а правильней было бы только в конструкторе реализовывать, чтоб гарантировать иммутабельность компаратора.
Иначе работа сортировки будет зависеть от алгоритма сортировки, думаю, сам понимаешь, что такие эффекты "чреваты боком".
Здравствуйте, Курилка, Вы писали:
К>Плюс довольно странная с логической т.зр. сортировка (по меньшей мере для меня) — вот ты с ходу ответишь какой цвет больше пурпурный или оранжевый? К>Т.е. не вижу как построить для цветов отношение порядка, чтоб оно было понятным человеку и непротиворечивым.
ОФФТОП: Например, есть понятие "цветовой температуры", т.е. до какой температуры надо разогреть тело, чтобы оно светилось данным цветом. Используется в некотрых областях и при определенном навыке становится понятным и непротиворечивым
Здравствуйте, fmiracle, Вы писали:
F>Здравствуйте, Курилка, Вы писали:
К>>Т.е. не вижу как построить для цветов отношение порядка, чтоб оно было понятным человеку и непротиворечивым.
F>ОФФТОП: Например, есть понятие "цветовой температуры", т.е. до какой температуры надо разогреть тело, чтобы оно светилось данным цветом. Используется в некотрых областях и при определенном навыке становится понятным и непротиворечивым
Ну для каких-нибудь физиков плазмы и чего-нибудь подобного это будет логично, но вот обычным людям это будет далеко не близко
Плюс что-то читал по поводу, что пурпурного цвета в спектре нет и он получается смешением цветов из разных участков. (Фактически наш мозг что-то такое делает, уже не помню чётко подробности)
Но, блин, хватит уже оффтопить
Здравствуйте, Курилка, Вы писали: К>Откуда в компараторе состояние? Если этим ты описываешь какие-то параметры сортировки, то ещё понятно.
Ага. К>Но это состоянием не будет, а правильней было бы только в конструкторе реализовывать, чтоб гарантировать иммутабельность компаратора.
Ну, это конечно можно. Но я не уверен, что компаратор обязательно должен быть иммутабельным. В процессе сортировки его неизменность обеспечивается отсутствием меняющих состояние методов в его интерфейсе. Но совершенно необязательно требовать, чтобы компаратор в течение всего времени жизни был иммутабельным.
К>Иначе работа сортировки будет зависеть от алгоритма сортировки, думаю, сам понимаешь, что такие эффекты "чреваты боком".
Гм. Работа сортировки и так зависит от алгоритма сортировки.
... << RSDN@Home 1.2.0 alpha rev. 677>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
К>>Иначе работа сортировки будет зависеть от алгоритма сортировки, думаю, сам понимаешь, что такие эффекты "чреваты боком". S>Гм. Работа сортировки и так зависит от алгоритма сортировки.
Ты имеешь в виду порядок элементов которые согласно компаратору равны? Ну это само собой.
Я же про другое — когда результат компаратора будет зависеть от меняющегося состояния (зависимого от предыдущих сравнений). Но это вообще будет какой-то полный абзац, конечно