Почему частичные методы не могут быть общедоступными, если реализация находится в одной и той же сборке?
Согласно Документация MSDN для частичных классов:
Частичные методы неявно закрыты
Итак, вы можете иметь это
// Definition in file1.cs
partial void Method1();
// Implementation in file2.cs
partial void Method1()
{
// method body
}
Но вы не можете этого
// Definition in file1.cs
public partial void Method1();
// Implementation in file2.cs
public partial void Method1()
{
// method body
}
Но почему это? Есть ли какая-то причина, по которой компилятор не может обрабатывать публичные частичные методы?
Ответы
Ответ 1
Поскольку команда компилятора MS не требовала реализации этой функции.
Вот возможный сценарий того, что происходит внутри MS, потому что VS использует код gen для множества его функций, в один прекрасный день разработчик кода MS-кода решил, что ему/ей необходимо иметь частичные методы, чтобы генерируемый кодом api мог быть расширенным посторонними лицами, и это требование побудило команду компилятора действовать и доставлять.
Ответ 2
Частичные методы должны быть полностью разрешимы во время компиляции. Если их нет во время компиляции, они полностью отсутствуют на выходе. Вся причина, по которой работают частичные методы, заключается в том, что их удаление не влияет на поток API или программы за пределами вызывающего сайта одной строки (что также связано с тем, что они должны возвращать void).
Когда вы добавляете метод в свой общедоступный API, вы определяете контракт для других объектов. Поскольку вся точка частичного метода заключается в том, чтобы сделать его необязательным, вы в основном говорите: "У меня есть контракт, на который вы можете положиться. О, подождите, вы не можете полагаться на этот метод".
Для того, чтобы публичный API был разумным, частичный метод должен либо всегда быть там, либо всегда уходить - в этом случае он не должен быть частичным.
В теории, разработчики языка могли бы изменить способ обработки частичных методов, чтобы это разрешить. Вместо того, чтобы удалять их извне, их вызывали, они могли бы реализовать их с помощью заглушки (т.е. Метода, который ничего не делает). Это было бы не так эффективно, и это необязательно для варианта использования, предусмотренного частичными методами.
Ответ 3
Вместо того, чтобы спрашивать, почему они являются частными, давайте перефразируем вопрос на этот вопрос:
- Что произойдет, если частичные методы не являются частными?
Рассмотрим этот простой пример:
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
? Неужели это просто ничего не значит? Поднять a MethodNotFoundException
? Здесь очень трудно следовать принципу наименьшего удивления; почти все, что вы делаете, будет удивительным.
Или, возможно, ничего не должно произойти, когда вызывается HandleException
, потому что базовый метод не был реализован. Однако это не кажется очень интуитивным. Наш производный класс пошел и реализовал этот метод, и базовый класс пошел и вытащил коврик из-под него, не зная нас. Я легко могу представить, что какой-то слабый разработчик вытаскивает волосы, не в силах понять, почему его переопределенный метод никогда не выполняется.
Или, может быть, этот код не должен компилироваться вообще или должен вызывать предупреждение. Но у этого есть ряд собственных проблем. Самое главное, это нарушает контракт, предоставляемый частичными методами, в котором говорится, что пренебрежение внедрением никогда не должно приводить к ошибке компилятора. У вас есть базовый класс, который напевает в порядке, а затем, в силу того, что кто-то реализовал полностью действующий производный класс в какой-то совершенно другой части приложения, внезапно ваше приложение нарушено.
И я даже не начал говорить о возможности того, что базовые и производные классы находятся в разных сборках. Что произойдет, если вы ссылаетесь на сборку с базовым классом, который содержит "открытый" частичный метод, и вы пытаетесь переопределить его в производном классе в другой сборке? Является ли базовый метод там или нет? Что если, изначально, метод был реализован, и мы написали кучу кода против него, но кто-то решил удалить реализацию? Компилятор не имеет возможности отключить вызовы частичного метода от ссылок на классы, поскольку, насколько это касается компилятора, этот метод никогда не существовал в первую очередь. Это не так, это не в сборке сборника IL больше. Итак, теперь, просто удалив реализацию частичного метода, который, как предполагается, не имеет вредных эффектов, мы ушли и сломали целую кучу зависимого кода.
Теперь некоторые люди могут сказать: "Так что, я знаю, что я не собираюсь пытаться делать этот незаконный материал с частичными методами". Вы должны понимать, что частичные методы, подобно частичным классам, в первую очередь предназначены для упрощения задачи генерации кода . Очень маловероятно, что вы захотите написать частичный метод самостоятельно. С другой стороны, с машинным кодом, на самом деле довольно вероятно, что потребители кода захотят "ввести" код в разных местах, а частичные методы обеспечивают чистый способ сделать это.
И в этом проблема. Если вы представите возможность ошибок времени компиляции из-за частичных методов, вы создали ситуацию, в которой генератор кода генерирует код, который не компилирует. Это очень и очень плохая ситуация. Думайте о том, что бы вы сделали, если ваш любимый дизайнерский инструмент - скажем, Linq to SQL или разработчик Winforms или ASP.NET, вдруг начал выпускать код, который иногда не компилируется, все потому что какой-то другой программист создал какой-то другой класс, который вы даже никогда не видели, прежде чем это стало слишком близким с частичным методом?
В конце концов, это действительно сводится к гораздо более простому вопросу: что бы публичные/защищенные частичные методы добавили, что вы еще не можете выполнить абстрактными методами? Идея частичных заключается в том, что вы можете поместить их в конкретные классы, и они все равно будут компилироваться. Вернее, они не будут компилироваться, но они также не выдадут ошибку, они просто будут полностью проигнорированы. Но если вы ожидаете, что их вызывают публично, то они больше не являются "факультативными", и если вы ожидаете, что их переопределит в производном классе, тогда вы можете просто сделать его абстрактным или виртуальным и пустым. На самом деле нет никакого использования для открытого или защищенного частичного метода, кроме путаницы компилятора и бедного ублюдка, пытаясь понять все это.
Итак, вместо того, чтобы открыть этот ящик Pandora, команда сказала, что забудьте об этом: общедоступные/защищенные частичные методы не так уж и много полезны, поэтому просто сделайте их закрытыми. Таким образом, мы можем держать все в целости и сохранности. И это. Пока частичные методы остаются частными, их легко понять и не беспокоить. Пусть так будет!
Ответ 4
Если вы не определяете метод, что должен делать компилятор?
Если вы не определяете их, частичные методы вообще не будут скомпилированы.
Это возможно только потому, что компилятор знает, где все вызовы метода. Если метод не определен, он полностью удалит строки кода, вызывающие метод. (Вот почему они могут возвращать только void
)
Если этот метод является общедоступным, это не может работать, потому что метод может быть вызван другой сборкой, которую компилятор не контролирует.
Ответ 5
Ответы Рида и Слэка совершенно правильны, но вы, похоже, не хотите принимать их ответы.
Я попытаюсь объяснить, почему частичные методы реализуются с этими ограничениями.
Частичные методы предназначены для реализации определенных типов сценариев генерации кода с максимальной эффективностью как во время выполнения, так и в служебных данных метаданных.
Последняя часть является реальной причиной для них, поскольку они пытаются сделать их (в словах Эрикс) "Платить за игру".
Когда были добавлены частичные методы, JIT полностью способна вставить пустой метод и, следовательно, иметь нулевое время выполнения на сайтах вызовов. Проблема в том, что даже тогда есть затраты, связанные с тем, что метаданные для класса будут иметь эти пустые методы, увеличивая их размер (без необходимости), а также заставляя приложить больше усилий в процессе JITing, чтобы справиться с их оптимизацией.
В то время как вы можете не слишком беспокоиться об этой стоимости (и, действительно, многие люди вообще этого не заметят), это имеет большое значение для кода, в котором важны затраты на запуск или где диск/память ограничена. Возможно, вы заметили, что теперь обязательное использование .Net на Windows Mobile 7 и Zune, в этих областях раздувание по метаданным типа является значительной стоимостью.
Таким образом, частичные методы разработаны таким образом, что, если они никогда не используются, они имеют абсолютно нулевую стоимость, они перестают существовать в выходе каким-либо образом. Это связано с некоторыми существенными ограничениями, чтобы гарантировать, что это не приведет к ошибкам.
Взято из msdn page с моими нотами.
- ... метод должен возвращать void.
- Частичные методы могут содержать ref, но не параметры.
В противном случае удаление вызова для них может оставить проблему с undefined в том, что заменить назначение.
- Частичные методы не могут быть extern, потому что присутствие тела определяет, определяют ли они или реализуют.
- Вы можете сделать делегата для частичного метода, который был определен и реализован, но не для частичного метода, который только был определен.
Это следует из того факта, что компилятор должен знать, если он определен или нет, и, таким образом, безопасен для удаления. Это приводит нас к тому, что вам не нравится.
- Частичные методы являются неявно частными, и поэтому они не могут быть виртуальными.
Ваша металлическая модель этой функции заключается в том, что, если компилятор знает, что реализован частичный метод, он должен просто разрешить частичный метод быть общедоступным (и виртуальным в этом отношении), поскольку он может проверить, что вы реализовали этот метод.
Если вы хотите изменить эту функцию, у вас есть два варианта:
-
Принудительно применять все частные частные методы, не требующие выполнения.
- простые и вряд ли потребуют больших усилий, но затем любые такие методы уже не являются частичными в осмысленном смысле первоначального плана.
-
Любой объявленный общедоступный метод просто удаляется, если он не был реализован в сборке.
- Это позволяет применять частичное удаление.
- Для компилятора требуется гораздо больше усилий (чтобы обнаружить все ссылки на этот метод, а не просто смотреть в сам созданный класс).
- Реализация IntelliSense немного запуталась, если метод будет показан? высевается только тогда, когда ему дается определение?
- Разрешение перегрузки становится намного более сложным, так как вам нужно решить, является ли вызов такого метода без определения либо a) сбоем времени компиляции, либо b) приводит к выбору следующего лучшего варианта, если он доступен.
- Побочные эффекты в выражениях на сайтах вызовов уже сложны в частном случае. Это несколько смягчается предположением, что частичные реализации уже демонстрируют высокую степень сцепления. Это изменение увеличило бы потенциал для сцепления.
- У этого есть довольно сложные режимы сбоя, поскольку публичные методы могут быть молча удалены каким-то безобидным изменением в исходной сборке. Другие сборки, зависящие от этого, потерпят неудачу (во время компиляции), но с очень запутанной ошибкой (без значительных усилий это применимо даже к проектам в том же решении).
Решение 1 просто бессмысленно, оно добавляет усилия и не приносит пользы первоначальным целям. Хуже того, это может помешать исходным целям, так как кто-то, используя частичные методы таким образом, может не понимать, что они не уменьшают метаданные. Также кто-то может запутаться в ошибках, которые возникают из-за невозможности предоставить определение.
Это оставляет нам решение 2. Это решение включает в себя усилия (и каждая функция начинается с -100), поэтому она должна добавить некоторые убедительные преимущества чтобы получить его за -100 (и дополнительные негативы добавлены для путаницы, вызванной не дополнительными случаями краев).
Какие сценарии вы можете придумать, чтобы добиться положительных результатов?
Ваш мотивирующий пример в комментариях выше был "иметь комментарии/и/или атрибуты в другом файле"
Мотивация комментариев XML заключалась в том, чтобы увеличить местность документации и кода. Если их многословность высока, то для смягчения это существует. Мое мнение таково, что этого недостаточно, чтобы заслужить эту функцию.
Возможность выдвигать атрибуты в отдельное место не на мой взгляд, что полезно, на самом деле я думаю, что смотреть в двух местах более сбивает с толку. Нынешняя частная реализация также имеет эту проблему, но она неизбежна и снова несколько смягчается предположением, что высокая связь, которая не видна вне класса, не так плоха, как высокая связь, внешняя по отношению к классу.
Если вы можете продемонстрировать какую-то другую вескую причину, я уверен, что это было бы интересно, но негативы для преодоления значительны.
Ответ 6
Я прочитал все ваши комментарии, и я согласен с большинством выводов, однако у меня есть один сценарий, в котором, как мне кажется, будут использованы общественные/защищенные частичные методы БЕЗ потери первоначального намерения:
У меня есть генератор кода, который генерирует код сериализации и другой код котельной. В частности, он генерирует метод "Reset" для каждого свойства, так что дизайнер вроде VS может вернуть свое значение оригиналу. В коде для этого метода я генерирую вызов частичного метода Repaint().
Идея состоит в том, что если объект хочет, он должен написать код для него, а затем сделать что-то, иначе ничего не будет сделано, а производительность будет оптимальной.
Проблема в том, что иногда существует метод Repaint в объекте для целей OTHER, чем вызванный из сгенерированного кода, в тот момент, когда я объявляю тело метода, я должен иметь возможность сделать его внутренним, защищенным или общедоступным. Я определяю метод на этом этапе, и да, я задокументирую и т.д. Здесь, а не в объявлении, которое я создал, но в том, что я сделал вручную. Тот факт, что он также был определен в сгенерированном коде, не должен влиять на это.
Я согласен, что определение модификаторов доступа в частичном методе не имеет смысла, когда вы объявляете метод без тела. Когда вы объявляете метод с телом, вы должны быть свободны объявлять его любым способом, которым хотите его. Я не вижу никакой разницы в сложности для компилятора, чтобы принять этот сценарий.
Обратите внимание, что в частичных классах это прекрасно, я могу оставить одно из частичных объявлений "голым" без модификаторов доступа, но я могу указать их в другом частичном объявлении того же класса. Пока нет противоречий, все в порядке.
Ответ 7
Не уверен, ответит ли это на ваш вопрос, он ответил на мой вопрос.
Частичные методы
Выдержки:
Если вы позволили что-то вернуть, и в свою очередь использовали это как вернуть значение из вашего CallMyMethods функции, вы столкнулись бы с проблемой когда частичные методы не были реализованы.
Ответ 8
Простые причины заключаются в том, что частичные методы не являются общедоступными, поскольку они являются деталями реализации. Они предназначены в первую очередь для поддержки сценариев, связанных с дизайном, и никогда не должны быть частью поддерживаемого публичного API. Здесь работает непубличный метод.
Предоставление частичных методов для публикации является особенностью. Особенности имеют неотъемлемые затраты, включая дизайн, тестирование, разработку и т.д. Частичные методы были лишь одним из многих функций, добавленных в очень упакованную Visual Studio 2008. Они были ограничены настолько малыми, насколько это возможно, чтобы соответствовать сценарию, чтобы уйти комната для более неотложных функций, таких как LINQ.
Ответ 9
partial
методы будут удалены компилятором, если у него нет реализации. Согласно моему пониманию, компилятор не сможет удалить методы partial
, если методы доступны как public
//partial class with public modifier
public partial class Sample
{
partial void Display();
}
public partial class Sample
{
partial void Display() { }
}
Мы сможем получить доступ к следующим методам public
, которые ниже будут ограничением для компилятора для удаления метода partial
, который не реализован.
// referred partial method which doesn't have implementation
var sample = new Sample();
sample.Display();