Зачем использовать частичные классы?

72

В моем понимании partialключевое слово делает только то, что позволяет разделить класс между несколькими исходными файлами. Есть ли причина для этого, кроме как для организации кода? Я видел, как это используется в сгенерированных классах пользовательского интерфейса.

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

Майкл К
источник
10
Это не совсем "целое ключевое слово", либо. Это контекстное ключевое слово . partialтолько означает что-то, когда это приходит раньше class. Вы можете использовать его как имя идентификатора в других частях кода и т. Д.
Дин Хардинг,
4
Если вы используете их, и это не для сгенерированного кода, я вас ненавижу. Не ты лично, а ты вообще. Я ненавижу искать код через частичные классы.
Стивен Эверс
3
Нетрудно «выследить код», все методы класса по-прежнему отображаются в раскрывающемся списке в VS независимо от того, какую часть партиала вы просматриваете. Другая навигация по коду тоже работает отлично (либо «умная» навигация в стиле R #, либо старый добрый shift-ctrl-f
Стив
2
Это недоразумение, что частичные классы должны быть в отдельных файлах.
Барт

Ответы:

110

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

Это касается не только пользовательского интерфейса, но и других технологий, таких как Linq-To-Sql или Entity Framework.

Ладислав Мрнка
источник
44
Кроме того, он позволяет держать ваш код под контролем версий, оставляя сгенерированный код вне контроля версий.
Фрэнк Шиарар
3
Хороший вопрос, я никогда не думал об этом. Я видел только злоупотребление на практике, хотя. Способ для (паршивых) программистов сохранять размер файла управляемым.
Мартин Уикман,
1
Лучшие примеры, которые я использовал: а) для контекста данных Linq to SQL, где вы хотите расширить классы; б) расширить классы, передаваемые с веб-сервисов. Ни в том, ни в другом случае это не является злоупотреблением и не является средством поддержания управляемости размеров файлов ... Я был бы радикально недоволен, увидев, что он используется таким образом (и предпримет шаги ...)
Murph
3
Классы WinForm используют его для сокрытия всего элемента управления, создающего код конструктора (особенно важно, потому что дизайнеру нравится случайным образом переупорядочивать весь код, который он создает, что приводит к огромным путаницам при diff / обвинении), и если вы работаете с XML, инструмент XSD может Снова создайте частичные классы из файла схемы, что позволит вам отделить ваш код от автоматически сгенерированной сортировки.
Дэн Нили,
2
@MartinWickman не обязательно паршивый. Это хорошая отправная точка для рефакторинга объектов Бога в унаследованном коде. Попробуйте очистить ландшафт, сначала разбив большие классы на отдельные файлы. (Я знаю, ваш комментарий очень старый)
Конрад Моравский
26

Как вы говорите, он часто используется для разделения сгенерированного кода. Часто это не имеет ничего общего с размером классов / файлов.

Преимущество разделения сгенерированного кода заключается в стиле. Сгенерированный код может быть довольно уродливым и нечитаемым и может не соответствовать многим стандартам кодирования (и проверкам StyleCop), но это нормально, никто не должен его читать или поддерживать напрямую. Таким образом, если вы «скрываете» его в другом файле, вы можете сосредоточиться на том, чтобы убедиться, что остальная часть класса соответствует стандарту, проходит проверки StyleCop и так далее.

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

Стив
источник
9
Я никогда не думал об использовании частичных классов для разделения реализаций интерфейса, это отличная идея.
Марк Бут
Это гораздо больше, чем стиль. Разработчикам генератора кода придется прыгать через обручи, чтобы позволить вам редактировать код в файле, управляемом генератором, и даже тогда это будет проблематично. Преимущество № 1, для меня, дает вам место для написания кода, который генератор не сможет / не сможет коснуться. Это довольно ощутимая выгода.
Даниил
19

Пару недель назад один из разработчиков, где я работаю, очень хорошо их использовал для рефакторинга огромных классов Бога, которые вышли из-под контроля и имеют множество открытых методов: путем разделения логических функций каждого бита. Класс на отдельные частичные классы. Вы можете физически разделить класс на более атомарные единицы, которые должны быть классами, не нарушая существующую функциональность, позволяя вам увидеть, что является общим, а что нет. На первом этапе вы можете легко разбить партиалы на их собственные независимые классы и реализовать их во всей базе кода. Я думал, что это была хорошая идея.

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


источник
1
Я вижу, как это может быть удобно. Является ли это оправданием для того, чтобы это было на языке или нет ... Я не знаю. Однако машинный код - это другая история.
Майкл К
2
Да, это не является оправданием для того, чтобы быть на языке, но это интересный способ их использования.
18

Я могу вспомнить несколько полезных сценариев, в которых партиалы имеют смысл, большинство из которых я использую сам в своих проектах:

  • Чтобы отделить сгенерированный код Tool / IDE / Designer от поддерживаемого вами кода. Хорошим примером является Form.Designer.csфайл, содержащий сгенерированный дизайнером код для приложений форм Windows. Многие другие форматы .NET также имеют некоторый код, сгенерированный инструментами, который потенциально может быть регенерирован при сборке вашего проекта, и, таким образом, все ваши пользовательские изменения будут удалены. Разделение поможет вам сохранить ваш код и изменения в безопасности от таких автоматических изменений.

  • Когда вы реализуете несколько интерфейсов с большим количеством кода в реализации. Я , как правило , использовать отдельный частичный файл для каждого такого интерфейса, называя это следующим образом : {Class}.{Interface}.cs. Мне легко увидеть в IDE, какие интерфейсы {Class}реализует и как.

  • Когда класс должен содержать один или несколько вложенных классов , особенно с достаточным количеством кода, который можно поместить в отдельный файл. Я придерживаюсь вышеупомянутого образца и использую {Class}.{NestedClass}.csсоглашение об именах для каждого вложенного класса. Подобная практика уже упоминается в ответе Virtlink .

  • Когда я пишу staticклассы, которые будут содержать методы расширения . Часто бывает так, чтобы предоставить одинаковую логику метода расширения для схожих классов или интерфейсов - как, например, Reverseдля общих и неуниверсальных коллекций. Я бы поместил все методы расширения для одного класса или интерфейса в отдельную часть статического класса. Например, у меня есть все методы расширения для IListинтерфейса в одном месте и те же методы, что и IList<T>в другом файле. Другой подход заключается в том, чтобы поместить один и тот же метод (все его перегрузки) в один и тот же фрагмент со всеми возможными классами для thisпараметра - например, иметь всеReverseреализации в одном файле. Это зависит от того, какой из них лучше оправдал бы разделение с точки зрения объема кода или каких-то внутренних соглашений, которых вы или ваша организация должны придерживаться.

  • Я не использую это, но я видел, что некоторым людям на C / C ++ нравится подход, который я опишу здесь: создайте частичное для класса только с частичными методами . Это напоминает способ C / C ++ для определения интерфейсов и отделения объявления метода от реализации.

  • Разделение по чисто логическим соображениям . Если вам доведется работать с большим классом, который объединяет в себе более одного логического набора операций, то вы можете разделить каждый логически связанный код на отдельный фрагмент. Обычно существование таких классов противоречит принципу разделения интересов , но это часто наблюдается в реальной жизни, особенно для старых кодовых баз. Другой пользователь Стив Эверс упомянул их в своем ответе на этот вопрос , ссылаясь на них именем бога объектов, Я бы лично использовал подход частичных классов для разделения кода до того, как произойдет рефакторинг таких действительно больших файлов, чтобы упростить мою работу и сделать рефакторинг более прозрачным. Это также уменьшит конфликты, которые я потенциально могу вызвать, когда задействованы системы контроля версий, такие как SVN.

Ивайло Славов
источник
14

Я не видел никого упомянутого: я использую, partialчтобы поместить вложенные классы в свои файлы.

Все мои файлы кода содержат только один класс, структуру, интерфейс или перечисление. Намного легче найти определение для объекта, когда имена файлов показывают название того, что вы ищете. А поскольку Visual Studio пытается сопоставить папки проекта с пространствами имен, имена файлов должны соответствовать классам.

Это также означает , что класс NestedClassвложенный MyClassбудет иметь свой собственный файл в моих проектах: MyClass.NestedClass.cs.

partial class MyClass
{
    private class NestedClass
    {
        // ...
    }
}
Даниэль А.А. Пельсмакер
источник
1
Интересно, почему кто-то проголосовал бы против этого ответа? Упомянутая практика не является ни анти-паттерном, ни причиной, о которой я знаю.
Ивайло Славов
1
Это также решает проблему сохранения небольших файлов классов (с точки зрения строк кода), в то же время позволяя правильно инкапсулировать код, принадлежащий внутреннему классу.
Redcalx
10

За исключением использования сгенерированного кода, я только когда-либо видел, чтобы они использовались в попытке прикрыть объекты бога . Попытка понять новую кодовую базу или перемещаться по нескольким исходным файлам, которые являются одним и тем же объектом, слишком раздражает.

Поэтому, когда вы спрашиваете, Why use partial classes?я отвечаю: если вы не используете сгенерированный код, не делайте этого.

Стивен Эверс
источник
+1. Это первый раз, когда я вижу, что эти типы объектов называют именем (объекты бога), и это даже официально. Никогда не поздно узнать что-то новое.
Ивайло Славов
6

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

  • Регенерируйте код без необходимости обнаружения изменений вручную в классах, в которые вы пишете (чтобы вы не перезаписывали эти части).
  • Исключить сгенерированные частичные классы из покрытия тестами, контроля источников, метрик и т. Д.
  • Используйте сгенерированный код, не заставляя вас основывать свою стратегию наследования на нем (вы все еще можете наследовать от других классов).
Декард
источник
1

Есть также несколько мест, где сгенерированные / подразумеваемые классы объявляются частичными, поэтому, если разработчик должен расширить их, он имеет полный доступ без необходимости возиться с наследованием и переопределением повсеместно. Посмотрите на сгенерированные классы во временной области asp.net после первого запуска сайта веб-форм под IIS, если вы хотите попытаться поймать некоторые примеры.

Билл
источник
1

Я могу думать о грязном использовании для них.

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

В основном вы хотите сделать множественное наследование, но C # не похоже. Итак, вы используете частичное для создания того, что по сути является #include в C / C ++;

Поместите всю функциональность в класс или используйте вспомогательный класс. Затем скопируйте помощник X раз и переименуйте его в частичный класс A, B, C. и поставить частичный на ваши классы A, B, C.

Отказ от ответственности: Да, это зло. Никогда не делай этого - особенно если это заставит других людей злиться на тебя.

отметка
источник
Примеси. Это может быть объединено с T4, чтобы избежать ручного копирования ...
Питер Тейлор
Как уже отмечалось, это очень похоже на миксин. Тем не менее, вы подходите к концепции, известной как «черты», и они благополучно справятся с тем, чего вы пытаетесь достичь. У меня есть описание, не зависящее от языка, на publius-ovidius.livejournal.com/314737.html Я искал чистую реализацию для C #, но не видел ее.
Овидий