Рыцари, жулики, Protected и Internal
При переопределении виртуального метода в C# вы требуете гарантий, что указанный модификатор доступа переопределенного метода (т.е. модификаторы public, internal, protected или protected internal (*)), будет точно соблюдаться при переопределении. За исключением одного случая. Я имею в виду раздел 10.6.4 спецификации языка C#, в котором сказано:
«объявление переопределенного метода не может изменять модификатор доступа виртуального метода. Однако если перегружаемый базовый метод имеет модификатор protectedinternal и объявлен в сборке, отличной от сборки, в которой производится переопределение метода, тогда модификатор доступа переопределенного метода должен быть protected.»
И что же, черт возьми, это означает? Конечно, если переопределяемый метод имеет модификатор доступа protectedinternal, тогда переопределение имеет смысл только в том случае, если переопределяемый метод будет иметь в точности тот же самый модификатор доступа: protectedinternal.
Я объясню смысл этого правила, но сначала короткое отступление.
На одном острове жили только рыцари и жулики. Рыцари говорят только правду и только правдиво отвечают на вопросы; жулики постоянно лгут и врут при ответе на вопросы. Если вы ближе посмотрите на жителей (так называемого) Острова Рыцарей и Жуликов вы можете быстро выяснить, относится конкретный житель к рыцарю или жулику, задав вопрос, ответ на который вам известен. Например, «два плюс два – четыре?» Рыцарь ответит «Да» (**), а жулик – «Нет». Жулики могут сказать: «моя мама – рыцарь мужского пола», что явно является ложью.
С первого взгляда может показаться, что не существует высказывания, которое может сказать и рыцарь и жулик. Поскольку рыцари говорят правду, а жулики – ложь, они одновременно не могут сказать одну фразу. Так ведь? Но на самом деле существует множество высказываний, которые могут высказать они оба. Можете придумать какое?
.
.
.
.
.
.
.
И рыцарь, и жулик могут сказать: «Я рыцарь».
Как такое может быть? Причина, по которой это возможно заключается в том, что местоимение «Я» указывает на разных людей, когда произносится разными людьми. Если Алиса, рыцарь, говорит «Я рыцарь», она говорит правду, поскольку «Алиса – рыцарь». Если Боб, жулик, говорит «Я рыцарь», он не утверждает, что «Алиса – рыцарь», а скорее высказывает ложное утверждение «Боб – рыцарь». По той же самой причине, оба, Алиса и Боб могут утверждать следующее: «Меня зовут Алиса».
Вот почему модификатор доступа переопределяемого метода в другой сборке не может быть “protected internal”. Модификатор “internal” играет роль местоимения в предыдущих примерах, он ссылается на текущую сборку. Использование этого модификатора в различных сборках означает разные вещи. Цель правила переопределения – гарантировать, что производный класс не сделает область доступности виртуального метода ни шире, ни уже.
Здесь может помочь аналогия. Предположим защищенный ресурс – это автомобиль, а сборка – это дом, человек – это класс, а его потомки – классы-наследники.
- У Алисы есть Mazda и она живет в Доме со своим хорошим другом Чарли.
- У Чарли есть ребенок – Диана, которая живет в Квартире.
- У Алисы есть ребенок – Элрой, который живет в Кондоминиуме со своим хорошим другом Грегом.
- У Элроя есть ребенок – внук Алисы – Фрэнк, который живет в Юрте.
Алиса дает пользоваться своей машиной всем жильцам Дома и своим потомкам. Т.е. к машине имеют доступ Алиса, Чарли, Элрой и Фрэнк.
У Дианы нет доступа к машине Алисы, потому что она не Алиса, не один из ее потомков и не живет в Доме. И не важно, что она является ребенком соседа Алисы.
У Грега нет доступа к машине Алисы по той же причине: он не Алиса, не один из ее потомков, не живет в Доме. И не важно, что он является соседом потомка Алисы.
Теперь мы подходим к главному вопросу. У Элроя нет прав расширить доступ к машине Алисы для Грега. Алиса является владельцем своей машины, и сказала: «машиной могу пользоваться я, мои потомки и мои соседи». У ее детей нет прав передавать доступ к ее машине за пределы установленного. Аналогично, у Элроя нет прав запретить доступ к ее машине Фрэнку; Фрэнк, потомок Алисы имеет права пользоваться машиной Алисы и Грег не может запретить ему делать это, сделав автомобиль «закрытым» (private).
Когда Элрой говорит о своем доступе к машине Алисы, он может только сказать: «Я даю доступ к ней себе и своим потомкам», поскольку это соответствует тому, что Алиса и так уже разрешила. Но он не может сказать: «Я даю доступ к машине Алисы себе, своим потомкам и всем жителям Кондоминиума».
-----------------
(*) Закрытые виртуальные методы запрещены в языке C#, что меня постоянно раздражает. Я определенно пользовался бы такой возможностью, если бы мы ее добавили.
(**) Конечно, предполагая, что они вообще отвечают на вопросы; вполне могут быть немые рыцари и жулики.