Ответ 1
Замечу, что здесь есть два вопроса. В будущем вы можете рассмотреть вопрос о публикации двух вопросов вместо объединения двух вопросов в одном. Когда вы совмещаете такие вопросы, часто случается, что только первый получает ответ.
Первый вопрос: "Почему частные виртуальные методы незаконны в С#?"
Вот аргументы против функции "частные виртуальные методы":
-
private virtual полезен только тогда, когда у вас есть вложенный производный класс. Это полезный шаблон, но гораздо менее распространенный, чем не-вложенная ситуация с производными классами.
-
Если вы хотите ограничить возможность переопределения метода в не-вложенных производных классах, вы можете сделать это, ограничив способность не вложенных классов выводить из базового класса; сделайте все конструкторы базового класса частными. Поэтому для предотвращения переопределения частного виртуального нет необходимости; защищенного виртуального достаточно, потому что только производные классы будут вложенными.
-
Если вы хотите ограничить возможность вызова метода в не-вложенном производном классе, вы можете сделать метод внутренним виртуальным, а затем сообщить сотрудникам, что он не использует этот метод. Это раздражает, если это не будет выполняться компилятором, но компилятор не применяет никаких других семантических ограничений на то, как должен использоваться метод; получение права семантики - это ваш бизнес, а не компилятор, и вы должны обеспечить его соблюдение с помощью соответствующих обзоров кода. Поэтому для предотвращения вызова не требуется виртуальный виртуальный компьютер; внутренних обзоров кода виртуального плюс.
-
Можно реализовать этот шаблон уже с существующими частями:
abstract class C { private int CF() { whatever; } private Func<int> f; public C() { f = CF; } private int F() { return f(); } private class D : C { private int DF() { whatever; } public D() { f = DF; } }
Теперь у меня есть метод F, который фактически является виртуальным, но может быть только "переопределен" производными вложенными классами.
Так как в каждом случае защищенный, внутренний или защищенный внутренний делает трюк, виртуальный виртуальный не нужен. Это почти никогда не будет правильным, поскольку вы должны быть уже готовы использовать шаблон вложенного производного класса. Таким образом, язык делает его незаконным.
Аргументы для:
Были случаи в реальном коде, когда я хочу, чтобы виртуальный метод был частной деталью реализации класса, который я хочу расширить как с помощью не-вложенных внутренних классов, так и вложенных внутренних классов. Необходимость принудительного применения инварианта, что внутренний метод не будет вызван моими коллегами, раздражает; Я бы хотел, чтобы это исполнялось компилятором без меня, чтобы перепрыгнуть через сумасшедшие обручи, как создать поле типа делегата и т.д.
Кроме того, просто вопрос согласованности и ортогональности. Кажется странным, что две вещи, которые должны быть независимыми - доступность и виртуальность - оказывают влияние друг на друга без необходимости.
Аргументы против этой функции довольно сильные. Аргументы для довольно слабы. Поэтому такой функции нет. Мне это очень понравилось, но я полностью понимаю, почему команда дизайнеров никогда меня не привлекала. Это не стоит затрат, и я бы не хотел, чтобы не поставлял лучшую функцию, потому что мы потратили бюджет на функцию, которая почти не приносит пользы.
Второй вопрос: "Почему в С# вы не можете переопределить частный виртуальный метод в производном не-вложенном классе?"
Существует несколько причин.
-
Потому что вы можете только переопределить то, что вы можете видеть. Частный метод представляет собой частную деталь реализации базового класса и не должен быть доступным.
-
Поскольку это имеет серьезные последствия для безопасности. Помните, что на С++ вы почти всегда компилируете код в приложение все сразу. У вас есть исходный код для всего; во многом по существу "внутренне" с точки зрения С++. В С# это совсем не так. Сторонние сборки могут легко получать публичные типы из библиотек и создавать новые расширения для этих классов, которые затем могут быть легко использованы вместо экземпляров базового класса. Поскольку виртуальные методы эффективно изменяют поведение класса, любой код, который зависит от соображений безопасности от инвариантов этого класса, должен быть тщательно разработан так, чтобы они не зависели от инвариантов, гарантированных базовым классом. Ограничение доступности виртуальных методов помогает гарантировать сохранение инвариантов этих методов.
-
Так как это дает другую форму проблемы хрупкого базового класса. С# был тщательно разработан, чтобы быть менее восприимчивым к проблеме хрупкого базового класса, чем другие языки OO. Если недопустимый виртуальный метод может быть переопределен в производном классе, тогда эта частная деталь реализации базового класса становится изменением при изменении. Провайдеры базовых классов должны иметь право изменять свои внутренние детали, не беспокоясь о том, что они сломали производные классы, которые зависят от них; в идеале должен поддерживаться только публичный документированный интерфейс к типу при изменении данных реализации.