Поделиться через


Разделяемые классы и методы (Руководство по программированию в 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илиstructinterface.
  • В определениях разделяемого типа могут присутствовать вложенные разделяемые типы, что показано в следующем примере:
    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#. Новые функции для частичных методов определяются в спецификации компонентов.

См. также