![]() |
Модераторы: Partizan, gambit |
![]() ![]() ![]() |
|
PashaPash |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1233 Регистрация: 3.1.2008 Репутация: 13 Всего: 49 |
Проблема такого подхода - слишком много "может" на необходимых для успеха необходимых действиях. Это все на самом деле "может подумать о последствиях переопределения в наследнике, может написать документацию, может упомянуть в ней использование, может быть прочитает инструкцию, может быть будет думать при переопределении". Т.е. для нормального результата нужно много активных действий со стороны 2-х людей. В случае с невиртуальными по умолчанию "может" превращается в "должен". Должет обявить хотя бы protected, должен сделать виртуальной, должен прочитать warning при переопределении, должен написать new. Второй подход IMHO, безопаснее. Скажем, на обсуждаемый вопрос - особенности реализации наследования - больше влияния оказал C++ и COM, чем Java. А в реально существующем C# есть раннее связывание для невиртуальных методов, и позднее - для невиртуальных. И вроде никто (почти) не жалуется. |
|||
|
||||
Dims |
|
||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
От того, что человек напишет слово virtual, он не станет должен написать инструкцию. Он всё равно может проигнорировать эту обязанность. C# требует, чтобы человек явно РАЗРЕШИЛ переопределять функции. А Джава требует, чтобы человек явно ЗАПРЕТИЛ переопределять функции, если это опасно. В обоих случаях человек может наплевать на написание инструкции, которая в обоих случаях необходима. Только при условии, что мы предполагаем, что сплошь и рядом встречаются случаи, когда а) в предке используется метод б) он переопределяется в наследнике в) это нежелательно Я считаю, что это маловероятно. По крайней мере, ситуацию, при которой это так, ещё никто не придумал. Зато гораздо более вероятна ситуация, когда функцию потребуется переопределить, но это будет запрещено, так как программист базового класса забыл написать virtual. Это защитило его от менее вероятной опасности, но так же и не дало возможность получить более вероятную выгоду. Фактически, вот я пишу класс. Если я не объявил класс как sealed, значит, я предполагаю, что от класса будут производить наследников. И если при этом я не подписал все функции как virtual, то я исключил возможность нормального наследования от своего класса. Иными словами, по умолчанию (то есть, без слов sealed и virtual) C# склоняет к созданию классов, непригодных к нормальному (с моей точки зрения) полиморфному наследованию. Отсюда следует, что разработчики C# считают, что наиболее частой является ситуация неполиморфного наследования, то есть, когда функции "затеняются". Отсюда и вопрос: когда это может пригодиться? То есть, когда может пригодиться именно затенение, а не переопределение. Ответа на этот вопрос до сих пор нами не найдено.
Здесь явная опечатка. Когда что? Ну, люди терпят даже такие хаотические языки, как PHP и Perl ![]() |
||||
|
|||||
PashaPash |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1233 Регистрация: 3.1.2008 Репутация: 13 Всего: 49 |
позднее - для виртуальных ес-но. Если ты не можешь увидеть такой ситуации, то значит ты пишешь код по принципу - "одна операция - один метод класса, который не вызывает остальные". Обычная ситуация. Есть метод А, он меняет внутренее состояние вызовом Б. Если методы виртуальные - значит ты готов поставить свою зарплату что кто-то другой всегда сможет корректно переопределить метод А и метод Б. Фактически, это значит что C# склоняет разработчиков ПОДУМАТЬ, перед тем как сделать метод частью public interface. Точно так же, как модификатор доступа private по умолчанию. Т.е. перед тем как позволить кому-то переопределить часть функционала, разработчик обязан обдумать последствия. Подумать что переопределяющий не будет иметь о внутреннем строении твоего класса вообще никакой информации. Ты не видишь негативных последствий - значит код за тобой никогда не дописывали нубы. Я пытался объяснить, что минимизация интерфейса по умолчанию - есть хорошо. Просто рассматривай виртуальные функции с той же стороны, что и как public. Если ты разрешил вызвать функцию - то кто-то ее вызовет. Если ты разрешил ее переопределить - то кто-то ее переопределит, причем совершенно не так, как ты этого ожидал. Ты сейчас отстаиваешь точку зрения которая полностью аналогична "а давайте сделаем public методы по умолчанию". Просто до этого писал на языке, где по умолчанию publiс, и теперь private с твоей точки зрения - ненормальная вещь. Просто пойми, что protected virtual это более широкий досуп к классу, чем public. А "неполиморфным наследованием" - возможностью затенения. Оно используется в исключительных случаях (читай - никогда). Некоторые сидят на JavaScript, с только public методами. И потом им private делает невозможной нормальную (с их точки зрения) работу с объектами ![]() |
|||
|
||||
Dims |
|
||||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
Если ты можешь, то опиши её пжлст прямо здесь.
К чему этот пафос? Ещё раз. Подумать -- это всегда полезно. А мы говорим о синтаксических умолчаниях. Как правило, синтаксические умолчания нужны для того, чтобы не указывать чего-то и в результате получить наиболее популярный результат. Если я умалчиваю sealed и умалчиваю virtual -- что я получаю? Класс, в котором при наследоваии методы можно только затенять. То есть, это наиболее распространённая ситуация с точки зрения разработчиков. Ну так опиши же, наконец, для чего она может быть нужна? Повторяю, это должна быть не какая-то ОДНА ИЗ ситуаций, а САМАЯ распространённая, так как она получается в результате умолчания. Вовсе не так же. private по умолчанию, потому что большую часть времени программист проводит в написании внутренней кухни класса. Поэтому private по умолчанию. Это разумно. А зачем по умолчанию non-virtual? Кому нужны кучи классов, в которых нельзя переопределять методы? В таком случае виртуальные функции надо вообще запретить. Ведь так получается по твоей логике. Я, конечно, не отрицаю, что в каких-то случаях злонамеренное или дурное переопределение функций может привести к нарушению работы. Но КАК ПРАВИЛО это не так. И ты это косвенно подтверждаешь тем, что не можешь привести примера, в котором переопределение виртуальной функции вредно.
Это тебе только так кажется.
Ну вот я мог бы объяснить таким людям, зачем нужен private, потому что у меня есть практика их использования. А ты можешь ли объяснить мне, зачем нужно virtual/nonvirtual? |
||||||
|
|||||||
PashaPash |
|
|||
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1233 Регистрация: 3.1.2008 Репутация: 13 Всего: 49 |
Это не пафос, это суровая правда :( Нет, ты получаешь класс в котором методы нельзя подменять без использования затенения. Это не значит что можно затенять методы без очень-очень веской причины. Пролистай стандартные классы .NET - в них очень неплохо продуман интерфейс и при этом - полно невиртуальных функций. Скажем, тот же BackgroundWorker - у него из виртуальных - только вызывающие события. Сравни его со своим воркером из соседнего топика. И сразу вылезет вот такой баг: У тебя в мелком классе всего пару виртуальных функций и уже есть родитель предьявляет требования к особенностям реализации наследника. Вот это - увеличение связности - и есть проблема виртуальных функций и наследования вообще. Наследование - не единственный способ построения дизайна, и даже не самый лучший с точки зрения этого самого дизайна. А виртуальные функции - не единственный способ организации полиморфизма. Не запретить, а сделать невиртуальными по умолчанию. А пример - да вот, прямо в твоем коде ![]() EnsureDataBound/EnsureChildControls который нужно вызывать из наследников, но ни в коем случае нельзя разрешить переопределить. Вообще, открой msdn, любой класс .net. Любая невиртуальная функция - пример. Если над ней подумать - то будут веские причины для ее невиртуальности. Кухня класса - это не только private методы. Это еще и детали реализации для public/protected. Вопрос точно такой же - уменьшение связности и увеличение инкапсуляции. Для этих двух вещей и private и non-virtual - одно и то же. Ок, давай, попробуй объяснить мне, зачем нужен private. Делаем все public. Мои аргументы: Если что - всегда можно прочитать про конкретный метод в документации. Я писал на JavaScript, там все public, все счастливы. Пришел в вашу Java, тут все private по умолчанию. Приведи мне пример, когда public вреден. Что у вас вообще за OOP такое, если нельзя подменить любой кусок имплементации? Какое-то ненастоящее. И на все аргументы - типа инкапсуляция, кухня класса, связность, минимизация интерфейса, скрытие деталей реализации - я буду отвечать "а вот в JavaScript все public, и ничего, очень удобно, и инкапсуляция не нарушаешься. Если что - всегда можно в документации написать.". |
|||
|
||||
diadiavova |
|
|||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Так разговор об умолчаниях что ли? Я то думал, что вопрос в том, зачем делать метод невиртуальным. А оказывается это вообще не вызывает вопросов, вопрос в том, почему они не виртуальны по-умолчанию.
А так ли это важно вообще? На мой взгляд это разумно, просто потому, что методы могут быть такой же внутренней кухней как private-члены(независимо от видимости), только их ещё можно предоставить в к использованию вне кухни, не мешая работе кухни. Вот и всё. Разработчики явы учли опыт, имевшийся в С++, а разработчики дотнета учли и опыт явы тоже. Результат мы видим. ЗЫ И в javascript есть private члены. -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
|||
|
||||
diadiavova |
|
|||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Да, и ещё.
Я тут в связи с тем, что говорим на эту тему долго, заинтересовался одним вопросом. Попробовал я поискать хоть какую-то возможность вызвать из производного класса, в котором переопределён метод, первоначальную версию этого метода. Прибегал к разным ухищрениям, но такого способа я не нашёл. Не думаю, что это хорошо во всех случаях. -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
|||
|
||||
Dims |
|
||||||||||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
Так зачем же подменять методы с использованием затенения? Где же тут баг?
Класс всегда предъявляет требования к тем, кто им пользуется, в частности -- к наследникам.
Ну наконец-то ты признался: в Си-диезе намеренно ограничено наследование, так как у наследования есть проблемы. ![]() Поподробнее пжлст. Только учти, что в бэкграунд воркере тот же, как ты выразился, "баг". Это плохой пример, потому что он -- частный. Если в каком-то редком случае метод нельзя переопределять, то это можно запретить отдельным ключевым словом. А по умолчанию переопределение должно быть разрешено.
Ну так подумай и выскажи свои соображения. Так ты что, не знаешь? Ещё раз. Я могу объяснить, зачем нужен private тем, кто им не пользовался, не привык и не знает, зачем он нужен. Потому что я пользовался, привык и знаю. Но, естественно, я не могу объяснить это тому, кто это и так знает, но собирается изображать, что не знает (ты) ![]() А с virtual -- другая ситуция. Я пришёл с Джавы и там никогда не пользовался затенением, не привык к этому и действительно не знаю, зачем оно нужно. Своими представлениями по опыту Си++ я уже поделился, и они мне кажутся неприменимыми в данном случае. А ты, вместо того, чтобы объяснить, или признаться, что сам не знаешь, просто аргументируешь на уровне раз большие дяди так сделали, значит это надо. Я про вред ничего не говорил. Я говорил про наиболее часто нужные вещи в работе. Добавлено через 7 минут и 17 секунд
Так это связано. Сперва (как я понял) аргументация была такая, мол, а почему бы и нет? Лозунг -- больше возможностей, плохих и хороших. Тогда я заметил, что невиртуальность сделана по умолчанию, то есть, трактуется не просто как одна из возможностей, а как важнейшая из них. Ведь если бы дело было только в том, чтобы предоставить ВОЗМОЖНОСТЬ делать невиртуальные функции, то можно было бы ввести слово "nonvirtual" и всё. А по умолчанию делать бы функции виртуальными.
Ну так как же можно помешать работе кухни с помощью виртуального метода? Я определяю класс и в нём создаю метод, который делает бред. Ну всё -- значит я создал дурацкий класс. Объекты этого класса будут дурацкими вне зависимости от того, переопределил ли я виртуальную функцию или просто с нуля написал функцию, которая бредит. Никаких проблем со связностью я тут не вижу. Никто ни с кем не связан. Есть просто сами по себе дурацкие объекты, и всё. Добавлено через 8 минут и 18 секунд Как это нельзя? А как же ключевое слово base? (Оно же super в Джаве). |
||||||||||||
|
|||||||||||||
diadiavova |
|
|||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Хорошее слово. Жаль только за пределами класса использовать нельзя. -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
|||
|
||||
Dims |
|
||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
Вот код:
вот вывод: Я невиртуальная в наследнике Я виртуальная в наследнике Я невиртуальная в базовом классе Я виртуальная в базовом классе Добавлено через 2 минуты и 40 секунд Вот такой код с теми же классами
даёт вот такой вывод: Я невиртуальная в базовом классе Я виртуальная в наследнике Добавлено через 4 минуты и 10 секунд То есть, чтобы вызвать невиртуальную функцию базового класса, нужно просто преобразовать тип. А виртуальную функцию базового класса вызвать нельзя, в этом и состоит защита: функция, написанная, когда производного класса ещё не было в природе, не имеет права с ним работать, если была переопределена. |
||||
|
|||||
diadiavova |
|
|||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Та я ж писал об этом. Когда на кухне используется метод, от него ожидается, что он будет делать дело определённым образом, а когда его переопределяют, он может делать это и по-другому. Это приходится учитывать. Не всегда возможно, да и лишний код, затраты на проверку условий и т. д. Я создаю метод для каких-то внутренних целей а кто-то его переопределяет, он работает не так как я задумал. И что в результате должно получиться? А человек, занимающийся наследованием моего класса не будет понимать откуда проблемы. У него ведь не обязательно должны быть исходные коды. Поэтому и по-умолчанию. А наследование это не только переопределение методов, а вообще расширение функциональности класса, определение новых методов например. Поэтому я не понимаю, к чему разговоры о нормальном наследовании и ненормальном. Добавлено через 3 минуты и 33 секунды Правильно, а если надо иметь такую возможность, то она есть - не делать метод виртуальным. Так что же: лучше бы её не было что ли? -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
|||
|
||||
Dims |
|
||||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
Во-первых, внутренний метод будет private. А во-вторых, ну и что? Вред от этого будет только тому классу, который создан этим кем-то. И этот вред будет равен вреду от просто неправильно написанной функции. Но мы же не может надеяться запретить людям писать неправильные функции! Добавлено через 3 минуты и 56 секунд
Так я ж говорю, что если бы это была просто возможность, которая когда-то (непонятно, когда) может пригодиться, то тогда ладно. Но это не просто возможность, а возможность по умолчанию. То есть, важная возможность. Поэтому, её надо понять, не так ли? Это сообщение отредактировал(а) Dims - 3.11.2008, 21:56 |
||||
|
|||||
diadiavova |
|
|||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Да, и кстати: затенение не является такой уж важной фичей. Это просто дополнительная возможность, своего рода компенсация того, что невиртуальные методы нельзя переопределить. Я уже писал, что затеняющий метод - это скорее перегрузка метода базового класса. На практике он почти не используется(поэтому и пример из практики привести трудно), но если это надо - то такая возможность есть.
Добавлено через 3 минуты и 4 секунды Да, но при этом он не будет знать в чём ошибка, так что лучше запретить. Та я то понимаю, только проблема в том, что мои аргументы мне кажутся убедительными, однако есть и другая точка зрения. -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
|||
|
||||
Dims |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1016 Регистрация: 21.11.2006 Репутация: 0 Всего: 11 |
Тогда почему она включается по умолчанию?
Я не понимаю. Из этого следует, что нужно вообще запретить виртуальные функции. Но Вы же не делаете такой вывод? Вы почему-то делаете вывод, что надо ПО УМОЛЧАНИЮ отключить. Но для такого вывода недостаточно одной лишь констатации вреда, нужно СРАВНИТЬ частоту вреда и пользы, и сделать по умолчанию то, что приносит больше пользы и меньше вреда. Добавлено через 4 минуты и 14 секунд Вот, например, вред от невиртуальной функции -- можно вызвать базовую функцию по отношению к производному классу. Вот ещё вред от невиртуальной функции -- невозможно вмешаться переопределить то, чего не предусмотрел или забыл предусмотреть разработчик базового класса. Кстати. Я тут покопался, и прихожу к выводу, что это и есть основной мотив. Микрософт, как коммерческий разработчик, заинтересован в том, чтобы его классы можно было использовать только так, как он предусмотрел. То есть, получать только то, за что заплачено. Если есть какая-то возможность развития класса, которую Микрософт не заметил и, стало быть, не взял за неё деньги при продаже, то эта возможность должна быть ограничена. |
|||
|
||||
diadiavova |
|
||||||
![]() Доктор Зло(диагност, настоящий, с лицензией и полномочиями) ![]() ![]() ![]() ![]() Профиль Группа: Модератор Сообщений: 5821 Регистрация: 14.8.2008 Где: В Коньфпольте Репутация: 31 Всего: 142 |
Потомучто альтернативой затенению является переопределение, а от него вреда может быть больше. Если человек включил в свой класс метод, не думая о том, что его метод будет вызван базовым классом, а он всё равно будет вызван - это может стать проблемой. Поэтому по-умолчанию.
А такой вывод я не делаю по двум причинам: 1.Знаю для чего нужны виртуальные методы. 2.Я уже миновал тот возраст, которому свойственен максимализм. Поэтому придерживаюсь средней линии. Вместо "всё или ничего" я предпочитаю "всё хорошо в меру и ко времени" Я так понимаю, что возможность вреда уже не оспаривается. О частоте вообще можно спорить долго, честно говоря не представляю себе как эту самую частоту вообще можно исчислить. Если речь идёт о неопытном программисте, то ему вообще запретить надо почти всё, а супердуперпрофи может не париться, у него и так всё получится. Я тут недавно одному парню советы давал по прорисовке в форме, так он в таком удивлении был, когда я объяснил ему, что надо вызвать Invalidate при изменении параметров прорисовки. Ему и в голову не пришло, что методы бывают виртуальными, и что метод определённый в базовом классе может вызывать прорисовку, которую он сам настряпал. Добавлено через 5 минут и 33 секунды
А можно и не вызвать, а вслучае с виртуальной - выбора нет. Иногда это хорошо, а иногда - плохо.
Если разработчик создал плохой класс(по забывчивости), не лучше ли использовать другие разработки(кто его знает, что он там ещё забыл) Коммерческий разработчик - не только майкрософт, а возможность такая есть не только у встроенных классов. -------------------- Хочешь получить мудрый совет - читай подписи участников форумов. Злой доктор Щасзаболит ![]() |
||||||
|
|||||||
![]() ![]() ![]() |
Прежде чем создать тему, посмотрите сюда: | |
|
Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов. Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :) Так же не забывайте отмечать свой вопрос решенным, если он таковым является :) Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Общие вопросы по .NET и C# | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |