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

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

Разделяемые классы

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

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

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

public partial class Employee
{
    public void DoWork()
    {
    }
}

public partial class Employee
{
    public void GoToLunch()
    {
    }
}

Ключевое слово 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 объявлены в одном определении разделяемого класса, а член PrintCoords — в другом определении разделяемого класса.

public partial class Coords
{
    private int x;
    private int y;

    public Coords(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

public partial class Coords
{
    public void PrintCoords()
    {
        Console.WriteLine("Coords: {0},{1}", x, y);
    }
}

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 модификатора.
  • Объявление метода не может содержать ни одного из следующих модификаторов:

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

Любой метод, не соответствующий всем этим ограничениям (например, метод public virtual partial void), должен предоставлять реализацию. Эта реализация может быть предоставлена генератором источника.

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

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

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

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

Дополнительные сведения см. в разделе "Частичные типы " и частичные методы в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#. Дополнительные функции для частичных методов определяются в спецификации компонентов.

См. также