Почему частичные методы не могут быть открытыми, если реализация находится в одной сборке? - PullRequest
17 голосов
/ 08 января 2010

Согласно Документация 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
}

Но почему это? По какой причине компилятор не может обрабатывать публичные частичные методы?

Ответы [ 9 ]

27 голосов
/ 08 января 2010

Частичные методы должны быть полностью разрешимы во время компиляции. Если их нет во время компиляции, они полностью отсутствуют в выводе. Причина того, что частичные методы работают, состоит в том, что их удаление не влияет на API или поток программ за пределами однострочного вызывающего сайта (и поэтому они должны возвращать void).

Когда вы добавляете метод в ваш публичный API - вы определяете контракт для других объектов. Поскольку весь смысл частичного метода состоит в том, чтобы сделать его необязательным, вы бы в основном говорили: «У меня есть контракт, на который вы можете положиться. Ой, подождите, вы не можете полагаться на этот метод».

Чтобы публичный API был разумным, частичный метод должен либо всегда быть там, либо всегда отсутствовать - в этом случае он не должен быть частичным.

Теоретически, разработчики языка могли бы изменить способ работы частичных методов, чтобы позволить это. Вместо того чтобы удалять их везде, где они были вызваны, они могли бы реализовать их, используя заглушку (то есть: метод, который ничего не делает). Это не было бы так эффективно и не является необходимым для варианта использования, предусмотренного частичными методами.

8 голосов
/ 19 марта 2010

Вместо того, чтобы спрашивать , почему они закрыты , давайте перефразируем вопрос следующим образом:

  • Что бы произошло, если бы частичные методы не были приватными?

Рассмотрим этот простой пример:

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, внезапно начал создавать код, который иногда не может скомпилироваться, потому что какой-то другой программист создал какой-то другой класс, который вы никогда раньше не видели, который стал слишком интимным с частичным методом?

В конце концов, все сводится к гораздо более простому вопросу: что бы добавили открытые / защищенные частичные методы, чего вы уже не можете достичь с помощью абстрактных методов? Идея частичных функций заключается в том, что вы можете поместить их в конкретные классы, и они все равно будут компилироваться. Или, скорее, они не будут компилироваться, но они также не будут выдавать ошибку, они просто будут полностью проигнорированы. Но если вы ожидаете, что они будут вызываться публично, то они больше не являются «необязательными», и если вы ожидаете, что они будут переопределены в производном классе, то вы можете просто сделать его абстрактным, виртуальным и пустым. На самом деле нет использования для публичного или защищенного частичного метода, кроме как для того, чтобы запутать компилятор и бедного ублюдка, пытающегося разобраться во всем этом.

Таким образом, вместо того, чтобы открыть ящик Пандоры, команда сказала: забудь об этом - публичные / защищенные частичные методы все равно мало используются, поэтому просто делай их приватными. Таким образом, мы можем сохранить все в безопасности и в здравом уме. И это. Пока частичные методы остаются приватными, их легко понять и не беспокоиться. Давайте так держать!

6 голосов
/ 24 марта 2010

Поскольку у команды компилятора MS не было требования для реализации этой функции.

Вот возможный сценарий того, что происходит внутри MS, потому что VS использует code gen для многих своих функций, однажды разработчик MS code gen решил, что ему нужно иметь частичные методы, чтобы код, сгенерированный API быть расширенным посторонними, и это требование заставило команду компилятора действовать и доставлять.

3 голосов
/ 08 января 2010

Если вы не определили метод, что должен делать компилятор?

Если вы не определите их, частичные методы не будут скомпилированы вообще .
Это возможно только потому, что компилятор знает, где находятся все вызовы метода. Если метод не определен, он полностью удалит строки кода, которые вызывают метод. (Это также, почему они могут только вернуть void)

Если метод публичный, это не может работать, потому что метод может быть вызван другой сборкой, которую компилятор не может контролировать.

2 голосов
/ 30 марта 2011

Я внимательно прочитал все ваши комментарии и согласен с большинством выводов, однако у меня есть один сценарий, в котором, как мне кажется, будут полезны открытые / защищенные частичные методы БЕЗ потери первоначального намерения:

У меня есть генератор кода, который генерирует код сериализации и другой код котельной пластины. В частности, он генерирует метод «Сброс» для каждого свойства, чтобы такой конструктор, как VS, мог вернуть свое значение к исходному. В коде этого метода я генерирую вызов частичного метода Repaint ().

Идея состоит в том, что если объект хочет, он может написать для него код, а затем что-то сделать, иначе ничего не будет сделано и производительность будет оптимальной.

Проблема в том, что иногда метод Repaint существует в объекте для целей ДРУГОЕ, чем при вызове из сгенерированного кода, в тот момент, когда я объявляю тело метода, я должен иметь возможность сделать его внутренним, защищенным или общедоступным. На этом этапе я определяю метод, и да, я буду документирован и т. Д. Здесь, не в декларации, которую я сгенерировал, а в той, которую я сделал вручную. Тот факт, что он также был определен в сгенерированном коде, не должен влиять на это.

Я согласен, что определение модификаторов доступа в частичном методе не имеет смысла, когда вы объявляете метод без тела. Когда вы объявляете метод WITH телом, вы можете свободно объявлять его так, как хотите. Я не вижу никакой разницы в сложности, чтобы компилятор мог принять этот сценарий.

Обратите внимание, что в частичных классах это прекрасно, я могу оставить одно из частичных объявлений «голым» без каких-либо модификаторов доступа, но я могу указать их в другом частичном объявлении того же класса. Пока нет противоречий, все в порядке.

2 голосов
/ 20 марта 2010

Ответы Рида и Слака совершенно верны, но вы, похоже, не желаете принимать их ответы.

Поэтому я попытаюсь объяснить , почему частичные методы реализованы с этими ограничениями.

Частичные методы предназначены для реализации определенных видов сценариев генерации кода с максимальной эффективностью, как во время выполнения, так и в накладных расходах метаданных. Последняя часть - настоящая причина для них, так как они пытаются заставить их (в словах Erics) «платить за игру».

Когда были добавлены частичные методы, JIT был полностью способен встроить пустой метод, и, таким образом, он не требовал времени выполнения на сайтах вызовов. Проблема заключается в том, что даже в этом случае возникают затраты, заключающиеся в том, что метаданные для класса будут содержать эти пустые методы, увеличивая их размер (без необходимости), а также заставляя прилагать дополнительные усилия в процессе JITing для их оптимизации.

Хотя вы можете не слишком беспокоиться об этих затратах (да и вообще многие вообще об этом не заметят), это действительно имеет большое значение для кода, где важна стоимость запуска или когда диск / память ограничены. Возможно, вы заметили, что теперь обязательное использование .Net на Windows Mobile 7 и Zune, в этих областях раздувание на метаданных типа стоит немалых затрат.

Таким образом, частичные методы разрабатываются таким образом, что, если они никогда не используются, они имеют абсолютно нулевую стоимость, они каким-либо образом перестают существовать в выходных данных. Это сопровождается некоторыми существенными ограничениями, чтобы гарантировать, что это не приведет к ошибкам.

Взято со страницы msdn с моими заметками.

  • ... метод должен возвращать void.
  • Частичные методы могут иметь параметры ref, но не out.

В противном случае удаление вызова для них может привести к неопределенной проблеме замены задания.

  • Частичные методы не могут быть внешними, потому что наличие тела определяет, являются ли они определяющими или реализующими.
  • Вы можете сделать делегатом частичный метод, который был определен и реализован, но не частичному методу, который был только определен.

Это вытекает из того факта, что компилятор должен знать, определен он или нет, и, следовательно, безопасен для удаления. Это приводит нас к тому, что вам не нравится.

  • Частичные методы неявно являются частными, и поэтому они не могут быть виртуальными.

Ваша металлическая модель этой функции такова, что, если компилятор знает, что частичный метод реализован, он должен просто позволить частичному методу быть открытым (и, в этом отношении, виртуальным), поскольку он может проверить, что вы реализовали метод.

Если вы хотите изменить функцию, у вас есть два варианта:

  1. Принудительно все не приватные частичные методы для требуют реализации.

    • просто и вряд ли потребует больших усилий, но тогда любые такие методы больше не являются частичными в значимом смысле первоначального плана.
  2. Любой метод, объявленный как public, просто удаляется, если он не был реализован в сборке.

    • Это позволяет применять частичное удаление
    • Компилятору требуется гораздо больше усилий (чтобы обнаружить все ссылки на метод, а не просто искать в самом составном классе)
    • Реализация IntelliSense немного запутана, должен ли метод быть показан? сеять только тогда, когда дано определение?
    • Разрешение перегрузки становится намного более сложным, так как вам нужно решить, является ли вызов такого метода без определения: а) сбой времени компиляции или б) приводит к выбору следующего лучшего варианта, если он доступен.
    • Побочные эффекты в выражениях на сайтах вызовов уже сложны в частном случае. Это несколько смягчается предположением, что частичные реализации уже демонстрируют высокую степень связи. Это изменение увеличит потенциал для связи.
    • Это имеет довольно сложные режимы сбоев в том, что публичные методы могут быть безмолвно удалены некоторыми безобидными изменениями в оригинальной сборке. Другие сборки, зависящие от этой, потерпят неудачу (во время компиляции), но с очень запутанной ошибкой (без значительных усилий это будет применимо даже к проектам в том же решении).

Решение 1 просто бессмысленно, оно добавляет усилий и не приносит пользы первоначальным целям. Хуже того, на самом деле это может помешать достижению первоначальных целей, поскольку тот, кто использует частичные методы таким образом, может не осознавать, что он не получает никакого сокращения в метаданных. Кроме того, кто-то может запутаться в ошибках, возникших из-за невозможности предоставить определение.

Это оставляет нам решение 2. Это решение требует усилий (и каждая функция начинается с -100 ), поэтому она должна добавить неоспоримые преимущества, чтобы получить ее выше -100 (и дополнительные негативы, добавленные для путаница, вызванная не дополнительными крайними случаями). Какие сценарии вы можете придумать, чтобы добиться положительных результатов?

Ваш мотивирующий пример в комментариях выше: «иметь комментарии / и / или атрибуты в другом файле»

Мотивация для комментариев XML состояла исключительно в том, чтобы увеличить местность документации и кода. Если их многословность высока, то существует план для смягчения этого. Мое мнение таково, что этого недостаточно, чтобы заслужить эту функцию.

Способность выдвигать атрибуты в отдельном месте, на мой взгляд, не очень полезна, на самом деле, мне кажется, что искать в двух местах более запутанно. Текущая реализация только для частного использования также имеет эту проблему, но она неизбежна и опять же несколько смягчается предположением, что высокая связь, которая не видна вне класса, не так плоха, как высокая связь, внешняя по отношению к классу.

Если вы сможете продемонстрировать какую-то другую убедительную причину, я уверен, что это было бы интересно, но негативные последствия, которые необходимо преодолеть, значительны.

1 голос
/ 12 мая 2017

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();
0 голосов
/ 24 марта 2010

Простые причины в том, что частичные методы не являются публичными, поскольку они являются деталями реализации. Они предназначены в первую очередь для поддержки сценариев, связанных с проектировщиком, и никогда не предназначались для поддержки общедоступного API. Непубличный метод прекрасно работает здесь.

Предоставление частичных методов для общего доступа - это особенность. Функциональные возможности сопряжены с затратами, включая проектирование, тестирование, разработку и т. Д. Частичные методы были лишь одной из многих функций, добавленных в очень упакованную Visual Studio 2008. Они были настолько малы, насколько это возможно, чтобы соответствовать сценарию, чтобы оставить комната для более актуальных функций, таких как LINQ.

0 голосов
/ 08 января 2010

Не уверен, если это ответит на ваш вопрос, он ответил на мой. Частичные методы

Выдержки:

Если бы вам разрешили что-то вернуть, и в свою очередь использовали это как возвращаемое значение из вашего CallMyMethods функция, вы столкнетесь с проблемами когда частичные методы не были реализован.

...