Разделяемые классы и методы в C#
Существует много ситуаций, когда вам может понадобиться разделить определение класса. Допустим, вы работаете над большим проектом, в котором задействовано множество разработчиков и программистов — всем нужно работать над одним классом одновременно. В таких случаях нам будут полезны разделяемые классы.
Разделяемые классы
В языке C# можно разделить определение класса между двумя или более исходными файлами. Каждый файл с исходным кодом содержит часть определения класса, а во время компиляции все части объединяются. Чтобы разделить определение класса, нужно использовать ключевое слово partial .
Пример 1. Выводим высоту и ширину
Мы напишем новый проект — HeightWeightInfo . Результат его работы — вывод в консоль высоты и ширины.
Внутри проекта содержится файл File1.cs с разделенным классом Record . В нем определены две целочисленные переменные — h и w . Также внутри реализован метод/конструктор Record , который присваивает значения переменным h и w .
Во втором файле File2.cs содержится точно такой же класс Record . Внутри него содержится лишь один метод — PrintRecord . Этот метод выводит в консоль значения переменных h и w .
В проекте метод main выглядит следующим образом:
В нем мы можем увидеть объект класса Record — myRecord . В качестве параметров поступают значения h и w , определенные в File1.cs — 10 и 15 соответственно.
Метод PrintRecord вызывается объектом myRecord , который объявлен в File2.cs .
Как видите, ключевое слово keyword помогает объединить атрибуты класса, объявленного в разных файлах, в один класс.
Когда partial класс может быть полезен:
- Вы работаете с большим проектом, в котором задействовано более одного разработчика. С помощью класса partial вы можете вместе работать с одним классом.
- Класс можно дополнять или редактировать без повторного создания исходного файла, которые автоматически генерируются IDE.
Что нужно запомнить о partial-классах
Ключевое слово partial указывает на то, что другие части класса могут объявлены в пространстве имен. Если мы хотим создать partial-класс, то необходимо использование ключевого слова partial . Все части класса должны быть принадлежать одному пространству имен и доступны во время компиляции в финальный тип. У всех переменных должен быть один и тот же модификатор доступа — например, private , public и т. д.
- Если какая-либо часть класса объявлена как abstract , то и финальный тип также считается abstract .
- Если какая-либо часть класса объявлена как sealed , то и финальный тип также считается sealed .
- Если в какой-либо части класса объявлен базовый класс, то финальный тип наследует этот класс.
- Любой член класса, объявленный в partial-классе, доступен остальным частям класса.
- Все части partial-класса должны содержаться в одном и том же пространстве имен.
Примечание. Модификатор partial недоступен в объявлениях делегатов и перечислений.
Разделяемые методы
Внутри partial-классов могут содержатся partial-методы. Одна из частей класса содержит сигнатуру метода, остальная часть его реализации может содержаться в той же части класса или другой. Если реализация не предоставлена, то метод и все его вызовы удаляются во время компиляции.
Пример 3. Рассматривает методы partial-класса Car
В качестве примера возьмем partial-класс Car , объявленный в File1.cs . В нем содержится три метода: InitializeCar() , BuildRim() и BuildWheels() . Среди них объявлен partial-класс InitializeCar .
Также в нашем проекте есть файл file2.cs . В нем содержится два метода: BuildEngine и InitializeCar . Метод Initialize — partial-метод, который был определен еще в file1.cs .
Partial class c что это
Классы могут быть частичными. То есть мы можем иметь несколько файлов с определением одного и того же класса, и при компиляции все эти определения будут скомпилированы в одно.
Например, определим в проекте два файла с кодом. Не столь важно как эти файлы будут называться. Например, PersonBase.cs и PersonAdditional.cs. В одном из этих файлов (без разницы в каком именно) определим следующий класс:
А в другом файле определим следующий класс:
Таким образом, два файла в проекте содержит определение одного и того же класса Person, которые содержат два разных метода. И оба определенных здесь класса являются частичными. Для этого они определяются с ключевым словом partial .
Затем мы можем использовать все методы класса Person:
Частичные методы
Частичные классы могут содержать частичные методы. Такие методы также опреляются с ключевым словом partial . Причем определение частичного метода без тела метода находится в одном частичном классе, а реализация этого же метода — в другом частичном классе.
Например, изменим выше определенные классы Person. Первый класс:
В первом классе определен метод Read() . Причем на момент определения первого класса неизвестно, что представляет собой этот метод, какие действия он будет выполнять. Тем не менее мы знаем список его параметров и можем вызвать в первом классе.
Второй класс уже непосредственно определяет тело метода Read() .
Стоит отметить, что по умолчанию к частичным методам применяется ряд ограничений:
Они не могут иметь модификаторы доступа
Они имеют тип void
Они не могут иметь out-параметры
Они не могут иметь модификаторы virtual, override, sealed, new или extern
Если же они не соответствуют какому-то из этих ограничений, то для них должна быть предоставлена реализация. Как, например, в следующем примере частичные методы применяют модификатор public :
What is a partial class?
What is and how can it be used in C#.
Can you use the same concept in Python/Perl?
8 Answers 8
A partial type (it doesn’t have to be a class; structs and interfaces can be partial too) is basically a single type which has its code spread across multiple files.
The main use for this is to allow a code generator (e.g. a Visual Studio designer) to «own» one file, while hand-written code is put in another.
I’ve no idea whether Python/Perl have the same capabilities, I’m afraid.
The c# partial class has been already explained here so I’ll just cover the python part. You can use multiple inheritance to elegantly distribute the definition of a class.
A partial class is simply a class that’s contained in more than one file. Sometimes it’s so that one part can be machine-generated, and another part user-edited.
I use them in C# when I’m making a class that’s getting a bit too large. I’ll put the accessors and constructors in one file, and all of the interesting methods in a different file.
In Perl, you’d simply have two (or more) files that each declare themselves to be in a package:
The concept of partial types have already been explained.
This can be done in python. As an example, do the following in a python shell.
Because python is a dynamic language you don’t need a concept like partial class. In python is possible to extend object with functionality in runtime so it possible to break class declaration into different files
A Partial type is a type whose declaration is separated across multiple files. It makes sense to use them if you have a big class, which is hard to handle and read for a typical developer, to separate that class definition in separate files and to put in each file a logically separated section of code (for instance all public methods and proprieties in one file, private in other, db handling code in third and so on..)
No you don’t have the same syntactical element in Python.
Python also has meta classes but that is more like a template class than a partial class. A good example of meta class usage is the Django ORM. All of your table models inherit from a base model class which also gets functionality included from a meta class. It is a pretty cool concept that enables an active record like pattern (is it full active record?).
Partial class comes handy when you have auto-generated code by some tool. Refer question Project structure for Schema First Service Development using WCF for an example.
You can put your logic in the partial class. Even if the auto-generated file is destroyed and recreated, your logic will persist in the partial class.
-
The Overflow Blog
Linked
Related
Hot Network Questions
Subscribe to RSS
To subscribe to this RSS feed, copy and paste this URL into your RSS reader.
Site design / logo © 2023 Stack Exchange Inc; user contributions licensed under CC BY-SA . rev 2023.7.31.43551
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
Programming stuff
Частичные классы (partial classes) (*) – это весьма простая конструкция языка C#, которая позволяет определить один класс в нескольких файлах (**). Это далеко не rocket science, но существует пара интересных сценариев их применения, которые являются не столь очевидными с первого взгляда. Но об этом позже, а начнем мы лучше с простого объяснения, что это такое и с чем его едят.
Общие сведения
Давайте вспомним старые добрые дни C# 1.0, когда спецификация языка была вдвое тоньше, возможностей языка было на порядок меньше, да и не было никаких LINQ-ов и всех остальных dynamic-ов (и вопросов на собеседовании, соответственно, тоже). Но даже тогда компилятор языка старался скрасить наши с вами серые будни и усердно выполнял всякую унылую работу, но делал он это не втихаря, путем генерации всяких там анонимных классов или методов, а путем генерации C# кода с помощь дизайнеров. И все бы ничего, но с этим подходом зачастую приходили и некоторые проблемы, которые, в основном сводились к ярому желанию разработчика расширить этот код и к ярому сопротивлению этого кода от подобного ручного вмешательства. Другими словами, внесение любых изменений в автосгенерированный код всегда вели к беде, ибо тут же прибивались компилятором при повторной генерации.
В те далекие дни существовало два простых способа борьбы с этой проблемой. Первый способ активно применялся, например, дизайнером Windows Forms, когда весь сгенерированный код формы помещался в отдельный регион, изменять который было чревато, а весь остальной пользовательский код располагался в оставшейся части файла (и, соответственно, класса). Другой подход наиболее часто применялся при работе с датасетами (да, это ORM для бедных, точнее и не ORM вовсе, но это уже и не важно), когда для расширения функционала нужно было создать класс-наследник и добавить в него весь необходимый дополнительный функционал.
Не сложно догадаться, что ни один из этих вариантов нельзя назвать очень уж удачным, так что не удивительно, что разработчики языка решили решить (во как!) эту проблему и предоставили возможность реализовывать один класс в нескольких файлах. В этом случае одним файлом ведает компилятор и творит с ним всяческие непотребства, ну а со вторым чем-то схожим занимается девелопер.
Итак, вот простой пример:
Обратите внимание, что только в одном файле указан наследник текущего класса (в данном случае, класс Form), и область видимости этого класса (ключевое слово public); это упрощает контроль этих аспектов пользователем, оставляя код, сгенерированный компилятором в нетронутом виде. Кроме того, как не сложно заметить, класс CustomForm все еще остается одним классом, что позволяет вызывать функции, определенные в одном файле (в данном случае, это функция InitializeComponent) из другого файла.
Частичные классы и выделение абстракций
Не пугайтесь страшного названия, rocket science в этой заметке так и не появится, просто более удачного названия подобрать не смог. Дело все в том, что частичные классы умеют делать нечто не совсем интуитивно понятное с первого взгляда. А именно, объявления частичных классов совпадать не должны, это значит, что в одном файле вы можете «объявить» реализацию классом некоторого интерфейса, а во втором файле – реализовать этот интерфейс, путем определения всех его методов (хотя можно часть методов реализовать в одном файле, а часть в другом; я думаю, что идея понятна):
И что в этом такого, скажете вы? Да, в общем-то, ничего особенного, но иногда это может очень здорово помочь при работе с автосгенерированным кодом. Давайте представим, что у нас есть некоторые сгенерированные компилятором классы (будь-то, классы, генерируемые по XSD, классы-сущности базы данных или что-то еще), и так уж вышло, что каждый из этих, совершенно не связанных между собой классов, содержит некоторую общую функциональность в виде набора свойств или методов, которые, в общем-то, делают одно и то же. Но поскольку все они генерируются компилятором, у вас (и у нас, кстати, тоже) нет возможности обрабатывать их обобщенным образом с помощью некоторого базового класса или интерфейса, в результате ваш код (и наш иногда тоже) начинает обрастать неприглядными конструкциями следующего вида:
Но, на самом деле, вам никто не мешает создать собственный интерфейс, который будет содержать всю общую функциональность, в виде свойств или методов, а затем просто добавить этот интерфейс к вашему определению частичного класса, не трогая при этом сгенерированный код. Тогда, если в каждом сгенерированном классе содержится свойство Name, вы можете добавить интерфейс IName со свойством Name и обрабатывать эти классы одним методом:
Частичные классы в юнит-тестировании
Чисто теоретически применение частичных классов не ограничивается автоматически сгенерированным кодом. Это самая обычная возможность языка программирования, и никто не мешает вам разбивать ваши собственные классы на несколько файлов. Почему «чисто теоретически»? Потому что, в большинстве случаев, если вы видите преимущества от использования частичных классов для реализации ваших собственных бизнес-сущностей, возникает некоторое подозрение, что эти самые сущности делают слишком много и, скорее всего, будет не лишним разбить их на несколько составляющих. (Хотя, нужно признать, из этого правила бывают и исключения.)
И хотя создание крупных классов в подавляющем большинстве случаев ни к чему хорошему не приводит, наличие достаточно увесистых классов юнит-тестов, в несколько раз превосходящих по объему тестируемые классы, дело обычное. Даже для небольшого класса может быть достаточно много сценариев тестирования, которые вполне можно сгруппировать по некоторому признаку (например, по некоторому аспекту поведения или чему-то в этом роде). В этом случае, мы можем разбить юнит-тест на несколько классов, и тогда придется запускать каждый из них независимо, а можем разбить один юнит-тест на несколько файлов с «частичными» классами. В этом случае, мы сможем запускать все тесты за раз, поскольку у нас будет всего один класс, но зато мы избавимся от громадных файлов. (Да, я знаю о наличии регионов, но мне подход с частичными классами нравится больше).
«Объединение» частичных классов в Solution Explorer-е
Внимательный разработчик наверняка давно уже заметил, что наша с вами любимая среда разработки, когда речь заходит об автоматически сгенерированном коде, умеет хитрым образом «объединять под одним крылом» частичные классы. Это выражается в том, что в Solution Explorer-е файлы, сгенерированные компилятором, находится «под» основным файлом, с которым работает разработчик. Но раз так, то наверняка мы сможем применять эту же технику для группировки наших собственных частичных классов.
И действительно, для этого достаточно немного поменять файл проекта и для зависимого файла добавить элемент с именем DependentUpon:
Помимо ручного редактирования файла проекта, можно порыться в Visual Studio Gallery и поискать расширение с подобной функциональностью. Как минимум одно расширение, с незамысловатым названием VSCommands 2010 поддерживает группировку нескольких файлов именно таким образом. После установки этого расширения в контекстном меню в Solution Explorer-е появятся дополнительные пункты меню “Group Items” и “Ungroup Items”, соответственно. Но не зависимо от того, каким именно способом вы будете «объединять» файлы, результат будет следующий (рисунок 1):
Рисунок 1 – Группировка файлов в Solution Explorer
Заключение
Ну вот, пожалуй, и все. Частичные классы – это действительно весьма простая возможность языка C#, которая в подавляющем большинстве случаев используется совместно с дизайнерами и автоматически генерируемым кодом. Но, тем не менее, существуют и другие интересные контексты использования этой возможности, которые могут быть не столь очевидными с первого взгляда.
(*) Да, как правильно заметили в комментариях, частичными бывают не только классы, но еще и структуры и интерфейсы. Так что все, что здесь говорится о классах, применимо еще и для структур с интерфейсами.