Private, protected, internal, etc…
Вот, на работе, столкнулись с простой задачей: кусочек стороннего кода надо запускать асинхронно. Кусочек может работать минут 10 и запускать его синхронно (в результате web запроса) как-то совсем неправильно. На всякий случай уточню, что исходники этого кусочка есть.
Вроде бы - чего тут сложного? А оказалось…
1. Кусок зависел от инициализированного окружения ASP.NET. Использовал аж одну функцию из всего API ASP.NET - меппинг виртуальных URL в физический путь. Без инициализации ASP.NET, меппинг кидал исключение.
2. Кусок нельзя было выдрать минимальными усилями (и дописать/переписать), так как он завязан на функционал, который помечен как internal (в шарпе internal означает что объект/метод доступен только из своей сборки и снаружи не виден), так что выдирание кусочка привело бы не только к размножению идентичного кода, а так же к выдиранию всех зависимостей
3. Инициализацию ASP.NET нельзя сфабриковать - все методы, которые инициализируют ASP.NET являются или internal или private.
4. У ASP.NET есть официальное API инициализации, которое “поднимает” новый application domain (весьма тяжелая операция) и с помощью remoting устанавливает связь с произвольным классом внутри этого application domain.
5. Кроме самой инициализации ASP.NET, кусочку потребовалась инициализация через его собственный фильтр запросов, который проверяет не был ли его фреймворк инициализирован и инициализирует, если надо. Что бы вызвать фильтр, надо повозиться с созданием HttpApplication, который хочет полноценной инициализации, типа той, которая приходит из ISAPI фильтра ASP.NET (а-ля http module в apache).
В сухом остатке:
1. Обдурить маленький кусок кода без полной инициализации ASP.NET оказалось нельзя
2. Инициализация ASP.NET очень тяжелая, так как ведет к полной компиляции ASP.NET приложения. Кроме того все приложение грузится в память, в нашем случае это примерно 100 мегов в памяти, несмотря на то, что оно совсем не надо.
3. Необходимо следить что бы application domain не умер во время работы. Если умер - прибить и создать заново (что накладно)
К чему я это все веду:
1. В питоне я бы просто вызвал функцию инициализации фреймворка напрямую. Работоспособность при переезде на новую версию фреймворка контролировал бы с помощью юнит-теста.
2. Ограничение доступа на уровне “ты сюда не ходи, ты сюда ходи” в таких ситуациях очень сильно мешает.