В отличие от Java, почему C # по умолчанию рассматривает методы как невиртуальные функции? Это скорее проблема производительности, чем другие возможные результаты?
Мне вспоминается абзац Андерса Хейлсберга о некоторых преимуществах, которые дает существующая архитектура. Но как насчет побочных эффектов? Действительно ли наличие не виртуальных методов по умолчанию - хороший компромисс?
c#
java
.net
virtual-functions
Бурку Доган
источник
источник
this
ссылка равна нулю. См. Здесь для получения дополнительной информации.Ответы:
Классы должны быть разработаны с учетом наследования, чтобы можно было использовать его преимущества. Наличие методов
virtual
по умолчанию означает, что каждая функция в классе может быть отключена и заменена другой, что на самом деле не очень хорошо. Многие даже считают, что занятия должны были бытьsealed
по умолчанию.virtual
методы также могут иметь небольшое влияние на производительность. Однако вряд ли это основная причина.источник
callvirt
простымcall
в коде IL (или даже дальше вниз по течению при JITting). Java HotSpot делает то же самое. В остальном ответ точен.Я удивлен, что здесь, кажется, существует такой консенсус, что не виртуальный по умолчанию - правильный способ делать что-то. Я собираюсь перейти на другую - я думаю, прагматичную - сторону забора.
Большинство оправданий мне напоминают старый аргумент «Если мы дадим вам силу, вы можете навредить себе». От программистов ?!
Мне кажется, что кодер, который не знал достаточно (или не имел достаточно времени), чтобы спроектировать свою библиотеку для наследования и / или расширяемости, - это кодер, который произвел именно ту библиотеку, которую мне, вероятно, придется исправить или настроить - именно та библиотека, в которой возможность переопределения будет наиболее полезной.
Количество раз, когда мне приходилось писать уродливый, отчаянный обходной код (или отказываться от использования и откатывать собственное альтернативное решение), потому что я не могу переопределить далеко, намного превышает количество раз, когда меня когда-либо укусили ( например, в Java), переопределив то, что дизайнер мог не учитывать.
Не виртуальный по умолчанию усложняет мне жизнь.
ОБНОВЛЕНИЕ: [совершенно правильно] было указано, что я на самом деле не ответил на вопрос. Итак - и с извинениями за опоздание ....
Я как бы хотел написать что-нибудь содержательное, вроде «C # по умолчанию реализует методы как не виртуальные, потому что было принято плохое решение, которое ценило программы выше, чем программисты». (Я думаю, что это может быть в какой-то мере оправдано, основываясь на некоторых других ответах на этот вопрос - например, о производительности (преждевременная оптимизация, кто угодно?) Или гарантиях поведения классов.)
Однако я понимаю, что просто изложу свое мнение, а не тот окончательный ответ, которого желает Stack Overflow. Конечно, подумал я, на самом высоком уровне окончательный (но бесполезный) ответ таков:
По умолчанию они не виртуальные, потому что разработчики языков должны были принять решение, и они выбрали именно это.
Теперь я предполагаю, что точную причину, по которой они приняли это решение, мы никогда не… ох, подождите! Стенограмма разговора!
Таким образом, может показаться, что ответы и комментарии здесь об опасностях переопределения API и необходимости явного проектирования для наследования находятся на правильном пути, но все они не имеют важного временного аспекта: основная забота Андерса заключалась в поддержании неявных классов или API контракт между версиями . И я думаю, что на самом деле он больше озабочен тем, чтобы позволить платформе .Net / C # изменяться под кодом, чем беспокоиться об изменении пользовательского кода поверх платформы. (И его «прагматическая» точка зрения прямо противоположна моей, потому что он смотрит с другой стороны.)
(Но разве они не могли просто выбрать виртуальный вариант по умолчанию, а затем добавить «final» в кодовую базу? Возможно, это не совсем то же самое ... и Андерс явно умнее меня, так что я позволю этому врать.)
источник
virtual
... Надстройка Visual Studio для кого-нибудь?Потому что слишком легко забыть, что метод может быть переопределен, а не разработан для этого. C # заставляет задуматься, прежде чем сделать его виртуальным. Считаю это отличным дизайнерским решением. Некоторые люди (например, Джон Скит) даже сказали, что классы должны быть запечатаны по умолчанию.
источник
Подводя итог тому, что говорили другие, есть несколько причин:
1. В C # есть много вещей в синтаксисе и семантике, которые взяты прямо из C ++. Тот факт, что методы, которые в C ++ по умолчанию не были виртуальными, повлияли на C #.
2- Наличие виртуального каждого метода по умолчанию является проблемой производительности, потому что каждый вызов метода должен использовать виртуальную таблицу объекта. Более того, это сильно ограничивает способность компилятора Just-In-Time встраивать методы и выполнять другие виды оптимизации.
3- Самое главное, что если методы не являются виртуальными по умолчанию, вы можете гарантировать поведение своих классов. Когда они по умолчанию виртуальные, например, в Java, вы даже не можете гарантировать, что простой метод получения будет работать так, как задумано, потому что его можно переопределить для выполнения каких-либо действий в производном классе (конечно, вы можете и должны сделать метод и / или финал класса).
Как упоминал Зифре, можно задаться вопросом, почему язык C # не пошел дальше и не сделал классы запечатанными по умолчанию. Это часть всей дискуссии о проблемах наследования реализации, которая является очень интересной темой.
источник
C # находится под влиянием C ++ (и других). C ++ по умолчанию не поддерживает динамическую отправку (виртуальные функции). Одним из (хороших?) Аргументов в пользу этого является вопрос: «Как часто вы реализуете классы, являющиеся членами иерархии классов?». Еще одна причина не включать динамическую отправку по умолчанию - это объем памяти. Класс без виртуального указателя (vpointer), указывающего на виртуальную таблицу , конечно, меньше, чем соответствующий класс с включенной поздней привязкой.
По поводу производительности не так-то просто сказать «да» или «нет». Причиной этого является компиляция Just In Time (JIT), которая является оптимизацией времени выполнения в C #.
Другой, аналогичный вопрос про " скорость виртуальных звонков .. "
источник
Простая причина - это затраты на дизайн и обслуживание в дополнение к затратам на производительность. Виртуальный метод имеет дополнительную стоимость по сравнению с невиртуальным методом, потому что разработчик класса должен спланировать, что происходит, когда метод переопределяется другим классом. Это имеет большое влияние, если вы ожидаете, что конкретный метод обновит внутреннее состояние или будет иметь определенное поведение. Теперь вы должны спланировать, что произойдет, когда производный класс изменит это поведение. В такой ситуации гораздо сложнее написать надежный код.
С невиртуальным методом у вас есть полный контроль. Во всем, что идет не так, виноват первоначальный автор. О коде гораздо легче думать.
источник
Если бы все методы C # были виртуальными, тогда vtbl был бы намного больше.
У объектов C # есть виртуальные методы, только если в классе определены виртуальные методы. Это правда, что все объекты имеют информацию о типе, которая включает эквивалент vtbl, но если виртуальные методы не определены, будут присутствовать только базовые методы Object.
@Tom Hawtin: Возможно, правильнее будет сказать, что C ++, C # и Java - все из семейства языков C :)
источник
Исходя из опыта работы с Perl, я думаю, что C # предопределил гибель каждого разработчика, который, возможно, захотел расширить и изменить поведение базового класса с помощью невиртуального метода, не заставляя всех пользователей нового класса знать о потенциально скрытых проблемах. подробности.
Рассмотрим метод Add класса List. Что, если разработчик захочет обновить одну из нескольких потенциальных баз данных всякий раз, когда конкретный список «добавлен»? Если бы «Добавить» было виртуальным по умолчанию, разработчик мог бы разработать класс «BackedList», который заменял бы метод «Добавить», не заставляя весь клиентский код знать, что это «BackedList» вместо обычного «списка». Для всех практических целей «BackedList» можно рассматривать как просто еще один «список» из клиентского кода.
Это имеет смысл с точки зрения большого основного класса, который может предоставлять доступ к одному или нескольким компонентам списка, которые сами поддерживаются одной или несколькими схемами в базе данных. Учитывая, что методы C # не являются виртуальными по умолчанию, список, предоставляемый основным классом, не может быть простым IEnumerable, ICollection или даже экземпляром List, но вместо этого должен быть объявлен клиенту как BackedList, чтобы гарантировать, что новая версия операции «Добавить» вызывается для обновления правильной схемы.
источник
B
естьA
. ЕслиB
требуется что-то другое,A
то это не такA
. Я считаю, что возможность переопределения в самом языке является недостатком дизайна. Если вам нужен другойAdd
метод, тогда ваш класс коллекции не являетсяList
. Пытаться сказать, что это подделка. Правильный подход здесь - композиция (а не подделка). Правда, вся структура построена на переопределяющих возможностях, но мне это просто не нравится.Конечно, это не проблема производительности. Интерпретатор Java Sun использует один и тот же код для отправки (
invokevirtual
байт-код), а HotSpot генерирует один и тот же код независимо от того, генерирует онfinal
или нет. Я считаю, что все объекты C # (но не структуры) имеют виртуальные методы, поэтому вам всегда понадобитсяvtbl
идентификация класса / runtime. C # - это диалект «Java-подобных языков». Предполагать, что это исходит от C ++, не совсем честно.Есть идея, что вы должны «спроектировать наследование или запретить его». Это звучит как отличная идея до того момента, когда у вас возникнет серьезное экономическое обоснование, которое нужно быстро исправить. Возможно, наследование от кода, который вы не контролируете.
источник
final
эффективнымиfinal
методами тривиальна. Также стоит отметить, что он может выполнять биморфное встраивание, то есть встроенные методы с двумя разными реализациями.