Вместо того, чтобы спрашивать , почему они закрыты , давайте перефразируем вопрос следующим образом:
- Что бы произошло, если бы частичные методы не были приватными?
Рассмотрим этот простой пример:
public partial class Calculator
{
public int Divide(int dividend, int divisor)
{
try
{
return dividend / divisor;
}
catch (DivideByZeroException ex)
{
HandleException(ex);
return 0;
}
}
partial void HandleException(ArithmeticException ex);
}
Давайте пока проигнорируем вопрос о том, почему мы бы сделали этот метод частичным, а не абстрактным (я вернусь к этому). Важно то, что это компилируется - и работает правильно - независимо от того, реализован метод HandleException
или нет. Если никто не реализует его, он просто съедает исключение и возвращает 0.
Теперь давайте изменим правила, скажем, что частичный метод может быть защищен:
public partial class Calculator
{
// Snip other methods
// Invalid code
partial protected virtual void HandleException(ArithmeticException ex);
}
public class LoggingCalculator : Calculator
{
protected override virtual void HandleException(ArithmeticException ex)
{
LogException(ex);
base.HandleException(ex);
}
private void LogException(ArithmeticException ex) { ... }
}
У нас тут небольшая проблема. Мы "переопределили" метод HandleException
, за исключением того, что пока нет метода для переопределения. И я имею в виду, что метод буквально не существует , он вообще не компилируется.
Что означает то, что наша база Calculator
вызывает HandleException
? Должен ли он вызывать производный (переопределенный) метод? Если да, то какой код генерирует компилятор для базового метода HandleException
? Должен ли он быть превращен в абстрактный метод? Пустой метод? А что происходит, когда производный метод вызывает base.HandleException
? Это должно просто ничего не делать? Поднять MethodNotFoundException
? Здесь действительно трудно следовать принципу наименьшего удивления; почти все, что вы делаете, будет удивительно.
Или, может быть, ничего не должно происходить при вызове HandleException
, потому что базовый метод не был реализован. Это не кажется очень интуитивным, хотя. Наш производный класс пошел и реализовал этот метод, а базовый класс пошел и вытащил коврик из-под него без нашего ведома. Я легко могу себе представить, как какой-то плохой разработчик тянет себя за волосы, не в силах понять, почему его переопределенный метод никогда не выполняется.
Или, может быть, этот код вообще не должен компилироваться или должен выдавать предупреждение. Но это имеет ряд собственных проблем. Наиболее важно то, что он нарушает контракт, предоставляемый частичными методами, что говорит о том, что пренебрежение реализацией никогда не должно приводить к ошибке компилятора. У вас есть базовый класс, который прекрасно гудит, а затем, в силу того, что кто-то реализовал полностью допустимый производный класс в какой-то совершенно другой части приложения , внезапно ваше приложение сломалось.
И я даже не заговорил о том, что базовые и производные классы могут находиться в разных сборках. Что произойдет, если вы ссылаетесь на сборку с базовым классом, который содержит «открытый» частичный метод, и вы пытаетесь переопределить его в производном классе в другой сборке? Базовый метод есть или нет? Что если первоначально метод был реализован, и мы написали кучу кода против него, но кто-то решил удалить реализацию? У компилятора нет возможности заглушить частичные вызовы методов ссылками на классы, потому что в том, что касается компилятора, этот метод вообще никогда не существовал . Его больше нет, его больше нет в IL скомпилированной сборки. Так что теперь, просто удалив реализацию частичного метода, который, как предполагается, не имеет вредных последствий, мы пошли и сломали целую кучу зависимого кода.
Теперь некоторые люди могут говорить: «Ну и что, я знаю, что я не собираюсь пытаться делать это незаконно с частичными методами». Вы должны понять, что частичные методы - как и частичные классы - в первую очередь предназначены для упрощения задачи генерация кода . Очень маловероятно, что вы когда-нибудь захотите написать частичный метод самостоятельно period . С другой стороны, с помощью машинно-сгенерированного кода вполне вероятно, что потребители кода захотят «внедрить» код в различных местах, а частичные методы обеспечивают чистый способ сделать это.
И в этом заключается проблема. Если вы вводите возможность ошибок во время компиляции из-за частичных методов, вы создали ситуацию, в которой генератор кода генерирует код, который не компилируется . Это очень, очень плохая ситуация. Подумайте, что бы вы сделали, если бы ваш любимый инструмент дизайнера - скажем, Linq to SQL, или дизайнер Winforms или ASP.NET, внезапно начал создавать код, который иногда не может скомпилироваться, потому что какой-то другой программист создал какой-то другой класс, который вы никогда раньше не видели, который стал слишком интимным с частичным методом?
В конце концов, все сводится к гораздо более простому вопросу: что бы добавили открытые / защищенные частичные методы, чего вы уже не можете достичь с помощью абстрактных методов? Идея частичных функций заключается в том, что вы можете поместить их в конкретные классы, и они все равно будут компилироваться. Или, скорее, они не будут компилироваться, но они также не будут выдавать ошибку, они просто будут полностью проигнорированы. Но если вы ожидаете, что они будут вызываться публично, то они больше не являются «необязательными», и если вы ожидаете, что они будут переопределены в производном классе, то вы можете просто сделать его абстрактным, виртуальным и пустым. На самом деле нет использования для публичного или защищенного частичного метода, кроме как для того, чтобы запутать компилятор и бедного ублюдка, пытающегося разобраться во всем этом.
Таким образом, вместо того, чтобы открыть ящик Пандоры, команда сказала: забудь об этом - публичные / защищенные частичные методы все равно мало используются, поэтому просто делай их приватными. Таким образом, мы можем сохранить все в безопасности и в здравом уме. И это. Пока частичные методы остаются приватными, их легко понять и не беспокоиться. Давайте так держать!