Разделяемые классы и методы (Руководство по программированию в C#)
Можно разделить определение класса, структуры, интерфейса или метода по двум или нескольким исходным файлам. Каждый исходный файл содержит часть определения класса или метода, а во время компиляции приложения все части объединяются.
Разделяемые классы
Существует несколько ситуаций, когда желательно разделение определения класса.
- Объявление класса по отдельным файлам позволяет нескольким программистам одновременно работать над ним.
- Вы можете добавить код в класс, не создавая исходный файл, который включает автоматически созданный источник. Visual Studio использует этот подход при создании форм Windows Forms, кода оболочки веб-службы и т. д. Можно создать код, который использует эти классы, без необходимости изменения файла, созданного в Visual Studio.
- Генераторы источников могут создавать дополнительные функциональные возможности в классе.
Чтобы разделить определение класса, используйте модификатор частичного ключевого слова. На практике каждый частичный класс обычно определяется в отдельном файле, что упрощает управление и расширение класса с течением времени.
В следующем Employee
примере показано, как класс может быть разделен на два файла: Employee_Part1.cs и Employee_Part2.cs.
// This is in Employee_Part1.cs
public partial class Employee
{
public void DoWork()
{
Console.WriteLine("Employee is working.");
}
}
// This is in Employee_Part2.cs
public partial class Employee
{
public void GoToLunch()
{
Console.WriteLine("Employee is at lunch.");
}
}
//Main program demonstrating the Employee class usage
public class Program
{
public static void Main()
{
Employee emp = new Employee();
emp.DoWork();
emp.GoToLunch();
}
}
// Expected Output:
// Employee is working.
// Employee is at lunch.
Ключевое слово partial
указывает, что другие части класса, структуры или интерфейса могут быть определены в пространстве имен. Все части должны использовать ключевое слово partial
. Для формирования окончательного типа все части должны быть доступны во время компиляции. Все части должны иметь одинаковые модификаторы доступа, например public
, private
и т. д.
Если какая-либо из частей объявлена абстрактной, то весь тип будет считаться абстрактным. Если какая-либо из частей объявлена запечатанной, то весь тип будет считаться запечатанным. Если какая-либо из частей объявляет базовый тип, то весь тип будет наследовать данный класс.
Все части, указывающие базовый класс, должны быть согласованы друг с другом, а части, не использующие базовый класс, все равно наследуют базовый тип. Части могут указывать различные базовые интерфейсы, и окончательный тип будет реализовывать все интерфейсы, перечисленные во всех разделяемых объявлениях. Любые члены класса, структуры или интерфейса, объявленные в разделяемом объявлении, доступны для всех остальных частей. Окончательный тип представляет собой комбинацию всех частей, выполненную во время компиляции.
Примечание.
Модификатор partial
недоступен в объявлениях делегатов или перечислений.
В следующем примере показано, что вложенные типы могут быть частичными, даже если тип, вложенный внутри, не является частичным.
class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
Во время компиляции атрибуты определений разделяемого типа объединяются. В качестве примера рассмотрим следующие объявления:
[SerializableAttribute]
partial class Moon { }
[ObsoleteAttribute]
partial class Moon { }
Они эквивалентны следующим объявлениям:
[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }
Следующие элементы объединяются из всех определений разделяемого типа:
- КОММЕНТАРИИ XML. Однако если оба объявления частичного члена включают примечания, будут включены только комментарии от реализующего члена.
- интерфейсы
- атрибуты параметров универсального параметра
- атрибуты классов
- members
В качестве примера рассмотрим следующие объявления:
partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }
Они эквивалентны следующим объявлениям:
class Earth : Planet, IRotate, IRevolve { }
Ограничения
При работе с определениями частичного класса существует несколько правил:
- Все определения разделяемого типа, являющиеся частями одного типа, должны изменяться с использованием типа
partial
. Например, следующие объявления класса приведут к появлению ошибки:public partial class A { } //public class A { } // Error, must also be marked partial
partial
Модификатор может отображаться непосредственно перед ключевым словомclass
илиstruct
interface
.- В определениях разделяемого типа могут присутствовать вложенные разделяемые типы, что показано в следующем примере:
partial class ClassWithNestedClass { partial class NestedClass { } } partial class ClassWithNestedClass { partial class NestedClass { } }
- Все определения разделяемого типа, являющиеся частями одного и того же типа, должны быть определены в одной сборке и в одном модуле (EXE-файл или DLL-файл). Частичные определения не могут охватывать несколько модулей.
- Имя класса и параметры универсального типа должны соответствовать всем определениям разделяемого типа. Универсальные типы могут быть разделяемыми. Все объявления разделяемого типа должны использовать одинаковые имена параметров в одном и том же порядке.
- Следующие ключевые слова для определения частичного типа являются необязательными, но если они присутствуют в одном определении частичного типа, то же самое должно быть указано в другом частичном определении для того же типа:
Дополнительные сведения см. в разделе Ограничения параметров типа.
Примеры
В следующем примере поля и конструктор Coords
класса объявляются в одном определении разделяемого класса (Coords_Part1.cs
), а PrintCoords
метод объявляется в другом определении разделяемого класса (Coords_Part2.cs
). Это разделение показывает, как частичные классы можно разделить на несколько файлов для упрощения обслуживания.
// This is in Coords_Part1.cs
public partial class Coords
{
private int x;
private int y;
public Coords(int x, int y)
{
this.x = x;
this.y = y;
}
}
// This is in Coords_Part2.cs
public partial class Coords
{
public void PrintCoords()
{
Console.WriteLine("Coords: {0},{1}", x, y);
}
}
// Main program demonstrating the Coords class usage
class TestCoords
{
static void Main()
{
Coords myCoords = new Coords(10, 15);
myCoords.PrintCoords();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
// Output: Coords: 10,15
В следующем примере показано, что можно также разработать разделяемые структуры и интерфейсы.
partial interface ITest
{
void Interface_Test();
}
partial interface ITest
{
void Interface_Test2();
}
partial struct S1
{
void Struct_Test() { }
}
partial struct S1
{
void Struct_Test2() { }
}
Частичные члены
Частичный класс или структуру может содержать частичный элемент. Одна часть класса содержит сигнатуру члена. В той же или в другой части можно определить реализацию.
Реализация не требуется для частичного метода, если подпись подчиняется следующим правилам:
- Объявление не включает модификаторы доступа. Метод имеет
private
доступ по умолчанию. - Тип возвращаемого значения —
void
. - Ни один из параметров не имеет
out
модификатора. - Объявление метода не может содержать ни одного из следующих модификаторов:
Метод и все вызовы метода удаляются во время компиляции при отсутствии реализации.
Любой метод, который не соответствует всем этим ограничениям, включая свойства и индексаторы, должен предоставлять реализацию. Эта реализация может быть предоставлена генератором источника. Частичные свойства нельзя реализовать с помощью автоматически реализованных свойств. Компилятор не может различать автоматическое свойство и объявление частичного свойства.
Начиная с C# 13, объявление реализации для частичного свойства может использовать свойства с поддержкой полей для определения объявления реализации. Резервное свойство поля предоставляет краткий синтаксис, в котором field
ключевое слово обращается к компилятору, синтезированному поле резервной копии для свойства. Например, можно написать следующее:
// in file1.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get; set; }
}
// In file2.cs
public partial class PropertyBag
{
// Defining declaration
public partial int MyProperty { get => field; set; }
}
Вы можете использовать field
либо в методе set
get
доступа, либо в обоих вариантах.
Внимание
Ключевое field
слово — это предварительная версия функции в C# 13. Для использования контекстного ключевого field
слова необходимо использовать .NET 9 и задать <LangVersion>
элемент preview
в файле проекта.
Следует тщательно использовать функцию field
ключевого слова в классе с именем field
поля. Новое field
ключевое слово тенирует поле с именем field
в области доступа к свойствам. Можно изменить имя переменной field
или использовать @
маркер для ссылки на field
идентификатор как @field
. Дополнительные сведения см. в спецификации компонента для ключевого field
слова.
Частичные методы позволяют реализации одной части класса объявлять член. Реализующий другую часть класса может определить этот элемент. Существует два сценария, в которых это разделение полезно: шаблоны, создающие стандартный код и генераторы источников.
- Код шаблона: шаблон резервирует имя и подпись метода, чтобы созданный код мог вызвать метод. Эти методы придерживаются ограничений, которые позволяют разработчику решить, следует ли реализовать этот метод. Если метод не реализован, компилятор удаляет сигнатуру метода и все вызовы метода. Вызовы метода, включая любые результаты, которые могли бы произойти от оценки аргументов в вызовах, не имеют эффекта во время выполнения. Поэтому любой код в частичном классе может свободно использовать частичный метод, даже если реализация не предоставлена. Если метод вызывается, но не реализуется, не возникает никаких ошибок во время компиляции или времени выполнения.
- Генераторы источников: генераторы источников предоставляют реализацию для членов. Разработчик может добавить объявление члена (часто с атрибутами, считываемыми генератором источника). Разработчик может написать код, вызывающий эти элементы. Генератор источника выполняется во время компиляции и обеспечивает реализацию. В этом сценарии ограничения для частичных членов, которые могут быть не реализованы, часто не следуют.
// Definition in file1.cs
partial void OnNameChanged();
// Implementation in file2.cs
partial void OnNameChanged()
{
// method body
}
- Объявления частичных элементов должны начинаться с частичного контекстного ключевого слова.
- Частичные подписи членов в обеих частях частичного типа должны соответствовать.
- Частичный член может иметь статические и небезопасные модификаторы.
- Частичный член может быть универсальным. Ограничения должны совпадать с определением и реализацией объявления метода. Имена параметров и типов не должны совпадать в объявлении реализации, как и в определяемом.
- Делегат можно сделать делегатом к частичному методу, определенному и реализованном, но не к частичному методу, который не имеет реализации.
Спецификация языка C#
Дополнительные сведения см. в разделе "Частичные типы " и частичные методы в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#. Новые функции для частичных методов определяются в спецификации компонентов.