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


Частичные классы и члены (руководство по программированию на C#)

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

Частичные классы

Существует несколько ситуаций, когда желательно разделение определения класса.

  • Объявление класса по отдельным файлам позволяет нескольким программистам одновременно работать над ним.
  • Вы можете добавить код в класс, не воссоздавая исходный файл с автоматически сгенерированным кодом. Visual Studio использует этот подход при создании форм Windows Forms, кода оболочки веб-службы и т. д. Можно создать код, который использует эти классы, без необходимости изменения файла, созданного в Visual Studio.
  • Генераторы источников могут создавать дополнительные функциональные возможности в классе.

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

В следующем 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. Однако если оба объявления частичного члена включают примечания, будут включены только комментарии от реализующего члена.
  • интерфейсы
  • атрибуты универсального типа-параметра
  • атрибуты классов
  • члены

В качестве примера рассмотрим следующие объявления:

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() { }
}

Частичные члены

Частичный класс или структуру может содержать частичный элемент. Одна часть класса содержит сигнатуру члена. Реализацию можно определить в той же части или в другой части.

Реализация не требуется для частичного метода, если подпись подчиняется следующим правилам:

Метод и все вызовы метода удаляются во время компиляции при отсутствии реализации.

Любой член, который не соответствует всем этим ограничениям, включая конструкторы, свойства, индексаторы и события, должен предоставлять реализацию. Эта реализация может быть предоставлена генератором источника. Частичные свойства нельзя реализовать с помощью автоматически реализованных свойств. Компилятор не может различать автоматическое свойство и объявление частичного свойства.

Начиная с 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 как в методе доступа get, так и в методе доступа set, или в обоих.

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

  • Код шаблона: шаблон резервирует имя и подпись метода, чтобы созданный код мог вызвать метод. Эти методы придерживаются ограничений, которые позволяют разработчику решить, следует ли реализовать этот метод. Если метод не реализован, компилятор удаляет сигнатуру метода и все вызовы метода. Вызовы метода, включая любые результаты, которые могли бы произойти от оценки аргументов в вызовах, не имеют эффекта во время выполнения. Поэтому любой код в частичном классе может свободно использовать частичный метод, даже если реализация не предоставлена. Если метод вызывается, но не реализуется, не возникает никаких ошибок во время компиляции или времени выполнения.
  • Генераторы источников: генераторы источников предоставляют реализацию для членов. Разработчик может добавить объявление члена (часто с атрибутами, считываемыми генератором источника). Разработчик может написать код, вызывающий эти элементы. Генератор источника выполняется во время компиляции и обеспечивает реализацию. В этом сценарии ограничения для частично реализованных участников, которые могут не быть внедрены, часто не соблюдаются.
// Definition in file1.cs
partial void OnNameChanged();

// Implementation in file2.cs
partial void OnNameChanged()
{
  // method body
}
  • Объявления частичных элементов должны начинаться с контекстного ключевого слова partial.
  • Частичные подписи членов в обеих частях частичного типа должны соответствовать.
  • Частичный член может иметь статические и небезопасные модификаторы.
  • Частичный элемент может быть обобщённым. Ограничения должны совпадать с определением и реализацией объявления члена. Имена параметров и параметров типов не обязаны совпадать в объявлении реализации и в определяющем.
  • Можно создать делегат для частичного метода, который определен и реализован, но не для частичного метода, который не имеет реализации.

Спецификация языка C#

Дополнительные сведения см. в разделе "Частичные типы " и частичные методы в спецификации языка C#. Спецификация языка является окончательным источником синтаксиса и использования языка C#. Новые функции для частичных элементов определяются в спецификациях функций для расширения частичных методов, частичных свойств и индексаторов, а также частичных событий и конструкторов.

См. также