Почему частные виртуальные методы недопустимы в C#?

Происходя из среды C++, это стало неожиданностью для меня. В C++ это - хорошая практика для создания виртуальных функций частными. Из http://www.gotw.ca/publications/mill18.htm: "Инструкция № 2: Предпочтите делать виртуальные функции частными".

Я также заключаю блог Eric Lippert в кавычки от Knights-knaves-protected-and-internal:

Частные виртуальные методы недопустимы в C#, который раздражает меня ни к какому концу. Я полностью использовал бы ту функцию, если бы у нас была она.

Я понимаю, что в C#, Вы не смогли бы переопределить частный виртуальный метод в полученном (но не вложенные) класс. Почему имеет место это? В C++ спецификатор доступа не имеет никакого отношения, можно ли переопределить функцию или нет.

39
задан user 1 May 2012 в 14:53
поделиться

6 ответов

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

Первый вопрос: «Почему частные виртуальные методы недопустимы в C #?»

Вот аргументы против возможности «частных виртуальных методов»:

  1. частные виртуальные методы полезны только тогда, когда у вас есть вложенный производный класс. Это полезный шаблон, но он встречается гораздо реже, чем ситуация с невложенным производным классом.

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

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

  4. Этот шаблон можно реализовать уже с существующими частями:

     абстрактный класс C
    {
    частный int CF () {что угодно; }
    private Func  f;
    общественный C () {f = CF; }
    частный int F () {return f (); }
    частный класс D: C
     {
    частный int DF () {что угодно; }
    общественный D () {f = DF; }
     }
    

    Теперь у меня есть метод F, который фактически виртуален, но может быть «переопределен» только производными вложенными классами.

Так как в каждом случае используется защищенный, внутренний или защищенный внутренний, частный виртуальный сервер не нужен. Это почти никогда не бывает правильным поступком, поскольку вы уже должны быть привержены использованию шаблона вложенного производного класса. Итак, язык делает это незаконным.

Аргументами являются:

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

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

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

Второй вопрос: «Почему в C # вы не можете переопределить частный виртуальный метод в производном невложенном классе?»

На это есть несколько причин.

  1. Потому что вы можете игнорировать только то, что видите. Частный метод является частным элементом реализации базового класса и не должен быть доступен.

  2. Потому что разрешение имеет серьезные последствия для безопасности. Помните, что в C ++ вы почти всегда компилируете код в приложение сразу. У вас есть исходный код для всего; с точки зрения C ++ в большинстве случаев все является «внутренним». В C # это совсем не так. Сторонние сборки могут легко получить доступ к общедоступным типам из библиотек и создать новые расширения для этих классов, которые затем можно будет беспрепятственно использовать вместо экземпляров базового класса.Поскольку виртуальные методы эффективно изменяют поведение класса, любой код, который по соображениям безопасности зависит от инвариантов этого класса, должен быть тщательно разработан, чтобы он не зависел от инвариантов, гарантированных базовым классом. Ограничение доступности виртуальных методов помогает гарантировать сохранение инвариантов этих методов.

  3. Потому что разрешение дает еще одну форму проблемы хрупкого базового класса. C # был тщательно разработан, чтобы быть менее восприимчивым к проблеме хрупкого базового класса, чем другие языки объектно-ориентированного программирования.Если недоступный виртуальный метод может быть переопределен в производном классе, тогда эта частная деталь реализации базового класса становится критическим изменением, если оно изменено. Провайдеры базовых классов должны иметь право изменять свои внутренние детали, не беспокоясь о том, что они сломали производные классы, которые зависят от них; в идеале при изменении деталей реализации необходимо поддерживать только общедоступный документированный интерфейс к типу.

38
ответ дан 27 November 2019 в 02:41
поделиться

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

Даже в C ++ производные классы не могут получить доступ к закрытым членам, но они могут ограничить видимость базового класса, указав видимость наследования:

class Derived : /*->>*/private/*<--*/ Base {
}

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

IMHO C # обеспечивает более сильные отношения IS-A через наследование одного базового класса, поэтому имеет смысл, что если у автомобиля есть двигатель, подкласс BMW не должен его скрывать.

C ++ поддерживает множественное наследование, которое является менее строгим отношением IS-A - оно почти похоже на отношение HAS-A, в котором вы можете ввести несколько несвязанных классов. Из-за возможности вводить несколько базовых классов вам нужен более жесткий контроль над видимостью всех из них.

2
ответ дан 27 November 2019 в 02:41
поделиться

Позвольте мне пояснить: C # - это не C ++.

C # был разработан спустя несколько десятилетий после C ++ и построен с использованием новейших идей, полученных за эти годы. По моему скромному мнению, C # хорошо определен и , наконец, правильно обрабатывает объектную ориентацию (imho). Он по какой-то причине включает в себя оператор internal и не позволяет «виртуализировать» и переопределять частные методы. По причине.

Все проблемы, описанные ранее (внутренние классы, переопределяющие частные виртуальные методы, использование абстрактного шаблона фабрики таким образом и т. Д.), Могут быть легко записаны другим способом с использованием интерфейсов и внутреннего заявление. Сказав это, я должен сказать, что это дело вкуса, нравится вам способ C ++ или способ C #.

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

Я занимаюсь разработкой на C ++ почти полтора десятилетия, и я никогда не сталкивался с необходимостью переопределения частных методов ... (я вижу, как появляются комментарии :-))

1
ответ дан 27 November 2019 в 02:41
поделиться

Я бы предположил, что причина в том, что internal virtual делает почти то же самое, что и private virtual, и несколько меньше запутывает тех, кто не знаком с идиомой private virtual.

В то время как только внутренние классы могут переопределять private virtual методы, только классы в вашей сборке могут переопределять internal virtual методы.

4
ответ дан 27 November 2019 в 02:41
поделиться

Поскольку к частным методам можно получить доступ ТОЛЬКО из определяющего их класса, частный виртуальный метод будет бесполезен. Вам нужен защищенный виртуальный метод. Доступ к защищенному методу может получить класс, определяющий его, и любые подклассы.

РЕДАКТИРОВАТЬ:

Предоставляя закрытые и защищенные ключевые слова, C # позволяет вам более детально контролировать ваши методы. То есть закрытые означает полностью закрытые, а защищенные - полностью закрытые отдельно от подклассов. Это позволяет вам иметь методы, о которых знает только ваш суперкласс, и методы, о которых могут знать подклассы.

15
ответ дан 27 November 2019 в 02:41
поделиться

В C # (и в CLI, насколько я видел) «частный» имеет довольно ясное и однозначное значение: «доступно только в этом классе». Концепция частных виртуальных машин все портит, не говоря уже о том, что пространства имен становятся чем-то вроде минного поля. Почему я должен заботиться о том, что вы назвали методом, который я даже не могу увидеть, и получать предупреждение компилятора о том, что случайно выбрал имя, которое вы уже зацепили для него?

2
ответ дан 27 November 2019 в 02:41
поделиться
Другие вопросы по тегам:

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