Почему методы в C# не являются автоматически виртуальными? [дубликат]

10
задан Community 23 May 2017 в 11:47
поделиться

12 ответов

Вам следует позаботиться о том, какие члены могут быть переопределены в производных классах.

Решение, какие методы сделать виртуальными, должно быть осознанным, хорошо продуманным решением, а не чем-то, что происходит автоматически, как и любые другие решения, касающиеся публичной поверхности вашего API.

20
ответ дан 3 December 2019 в 13:13
поделиться

Андерс Хейлсберг ответил на этот вопрос в этом интервью , и я цитирую:

Есть несколько причин. Один из них - производительность. Мы можем заметить, что, когда люди пишут код на Java, они забывают пометить свои методы как окончательные. Следовательно, эти методы являются виртуальными. Потому что они виртуальные, они не работают. Есть только накладные расходы на производительность, связанные с тем, что является виртуальным методом. Это одна проблема.

Более важная проблема - это управление версиями. Есть две точки зрения на виртуальные методы. Академическая школа мысли говорит: «Все должно быть виртуальным, потому что я, возможно, когда-нибудь захочу переопределить это». Прагматическая школа мысли, которая исходит из создания реальных приложений, работающих в реальном мире, говорит: «Мы должны быть очень осторожны о том, что мы делаем виртуальным ".

Когда мы делаем что-то виртуальное на платформе , мы даем очень много обещаний о том, как это будет развиваться в { {1}} будущее. Что касается невиртуального метода, мы обещаем, что при вызове этого метода возникнут x и y. Когда мы публикуем виртуальный метод в API, мы не только обещаем, что при вызове этого метода будут выполняться x и y. Мы также обещаем, что когда вы переопределите этот метод, мы вызовем его в этой конкретной последовательности в отношении этих других, и состояние будет { {1}} в том или ином инварианте.

Каждый раз, когда вы говорите виртуальный в API, вы создаете ловушку обратного вызова.Как разработчик фреймворка ОС или API, вы должны быть очень осторожны с этим. Вы не хотите, чтобы пользователи переопределяли и подключались к любой произвольной точке API, потому что вы не можете обязательно давать эти обещания. И люди могут не полностью понимать обещания, которые они дают, когда делают что-то виртуальным.

25
ответ дан 3 December 2019 в 13:13
поделиться

Когда кто-то наследует ваш класс, это дает им возможность изменять способ работы любого метода, когда его использует базовый класс. Если у вас есть метод, который вам абсолютно необходим для выполнения определенного действия в базовом классе, у вас не будет возможности запретить кому-либо изменить эту функциональность.

Вот один пример. Предположим, у вас есть функция, которая не должна возвращать ошибку. Кто-то входит и решает изменить его так, чтобы во вторник он генерировал исключение вне допустимого диапазона. Теперь код в базовом классе не работает, потому что что-то, от чего он зависел, изменилось.

1
ответ дан 3 December 2019 в 13:13
поделиться

Соглашение? Думаю, ничего больше. Я знаю, что Java автоматически делает методы виртуальными, а C # - нет, поэтому на каком-то уровне явно существуют разногласия относительно того, что лучше. Лично я предпочитаю C # по умолчанию - считаю, что переопределяющие методы гораздо менее распространены, чем , а не , переопределяющие их, поэтому было бы более лаконично определять виртуальные методы явно.

5
ответ дан 3 December 2019 в 13:13
поделиться

Помимо дизайна и ясности, невиртуальный метод также технически лучше по двум причинам:

  • Виртуальные методы вызываются дольше (поскольку среда выполнения должна перемещаться по виртуальной таблице поиска, чтобы найти фактический метод для вызова)
  • Виртуальные методы не могут быть встроены (поскольку компилятор не знает во время компиляции, какой метод в конечном итоге будет вызван)

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

6
ответ дан 3 December 2019 в 13:13
поделиться

Итак, ясно, разрешаете ли вы переопределение или метод, или принудительно скрываете метода (с помощью ключевого слова new ).

Принуждение вас к добавлению ключевого слова устраняет всю возможную двусмысленность.

2
ответ дан 3 December 2019 в 13:13
поделиться

Я считаю, что есть проблема с эффективностью, а также причины, о которых писали другие. Нет необходимости тратить циклы процессора на поиск переопределения, если метод не виртуальный.

1
ответ дан 3 December 2019 в 13:13
поделиться

Потому что это не Java

Серьезно, просто другая философия поддержки. Java хотела, чтобы расширяемость была по умолчанию, а инкапсуляция была явной, а C # хотел, чтобы расширяемость была явной, а инкапсуляция - по умолчанию.

1
ответ дан 3 December 2019 в 13:13
поделиться

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

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

Более того, они представляют угрозу для вашего дизайна. Дело не в том, что другие будут делать с вашим классом , а в хорошем дизайне объектов . Когда метод виртуальный, вы говорите, что можете отключить его и заменить другой реализацией. Как я говорю, вы должны относиться к такому методу как к врагу - вы не можете ему доверять. Нельзя полагаться ни на какие побочные эффекты. Вы должны установить очень строгий контракт для этого метода и придерживаться его.

Если учесть, что человек очень ленивое и забывающее существо , какой подход более разумный ?

Я никогда лично не использовал виртуальный метод в своем дизайн.Если есть какая-то логика, которую использует мой класс, и я хочу, чтобы она была взаимозаменяемой, я просто создаю для нее интерфейс. Этот интерфейс составляет вышеупомянутый контракт. Есть ситуации, когда вам действительно нужны виртуальные методы, но я думаю, что это довольно редко.

2
ответ дан 3 December 2019 в 13:13
поделиться

Перефразируя Эрика Липперта , одного из разработчиков C #: ваш код не будет случайно сломан при изменении исходного кода, полученного вами от третьей стороны. Другими словами, чтобы предотвратить проблему базового класса хрупкости .

Если метод должен быть виртуальным, если потому, что вы (предположительно) приняли сознательное решение разрешить замену функции, и спроектировали, протестировали и задокументировали это. Что произойдет, если, скажем, вы создали функцию «frob», а в какой-либо последующей версии создатели базового класса решат также сделать функцию «frob»?

3
ответ дан 3 December 2019 в 13:13
поделиться

См. Также ответ Андерса Хейлсберга (изобретателя C #) в Беседа с Андерсом Хейлсбергом, часть IV .

3
ответ дан 3 December 2019 в 13:13
поделиться

На самом деле, это плохая практика проектирования. Я имею в виду, не заботясь о том, какие методы можно переопределить, а какие нет. Вы должны всегда думать о том, что должно и должно быть отменено, так же как вы должны тщательно обдумывать, что должно, а что не должно быть публичным!

0
ответ дан 3 December 2019 в 13:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: