Разделяемые классы и методы (Руководство по программированию в 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-комментарии;

  • интерфейсы;

  • атрибуты параметров универсального типа;

  • атрибуты классов;

  • члены.

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

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

Они эквивалентны следующим объявлениям:

class Earth : Planet, IRotate, IRevolve { }

Ограничения

Имеется несколько правил, которые необходимо выполнять при работе с определениями разделяемого класса:

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

    public partial class A { }
    //public class tcA { }  // Error, must also be marked partial
    
  • Модификатор partial должен находиться непосредственно перед ключевыми словами class, struct или interface.

  • В определениях разделяемого типа могут присутствовать вложенные разделяемые типы, что показано в следующем примере:

    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
    partial class ClassWithNestedClass
    {
        partial class NestedClass { }
    }
    
  • Все определения разделяемого типа, являющиеся частями одного и того же типа, должны быть определены в одной сборке и в одном модуле (EXE-файл или DLL-файл). Разделяемые определения не могут находиться в разных модулях.

  • Имя класса и параметры универсального типа должны соответствовать всем определениям разделяемого типа. Универсальные типы могут быть разделяемыми. Все объявления разделяемого типа должны использовать одинаковые имена параметров в одном и том же порядке.

  • Приведенные ниже ключевые слова необязательно должны присутствовать в определении разделяемого типа, но если они присутствуют в одном определении разделяемого типа, то не должны конфликтовать с ключевыми словами, указанными в других определениях того же разделяемого типа:

Пример 1

Описание

В следующем примере поля и конструктор класса 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

Пример 2

Описание

В следующем примере показано, что можно также разработать разделяемые структуры и интерфейсы.

Код

partial interface ITest
{
    void Interface_Test();
}

partial interface ITest
{
    void Interface_Test2();
}

partial struct S1
{
    void Struct_Test() { }
}

partial struct S1
{
    void Struct_Test2() { }
}

Разделяемые методы

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

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

Разделяемые методы особенно полезны для настройки автоматически созданного кода. Они позволяют зарезервировать имя и сигнатуру метода, чтобы автоматически созданный код мог вызвать метод, а разработчик мог сам решить, реализовывать этот метод или нет. Как и разделяемые классы, разделяемые методы позволяют организовать совместную работу автоматически созданного кода и кода, созданного человеком, без дополнительных затрат во время выполнения.

Объявление разделяемого метода состоит из двух частей: определения и реализации. Они могут находиться в разных частях или в одной и той же части разделяемого класса. Если объявление реализации отсутствует, то компилятор оптимизирует код, удаляя как объявление определения, так и все вызовы метода.

// Definition in file1.cs
partial void onNameChanged();

// Implementation in file2.cs
partial void onNameChanged()
{
  // method body
}
  • Объявления разделяемого метода должны начинаться с контекстно-зависимого ключевого слова partial, а метод должен возвращать значение типа void.

  • Разделяемые методы могут иметь параметры ref, но не могут иметь параметры out.

  • Разделяемые методы неявно имеют модификатор private и поэтому не могут иметь модификатор virtual.

  • Разделяемые методы не могут иметь модификатор extern, поскольку наличие тела определяет, выполняется ли их определение или реализация.

  • Разделяемые методы могут иметь модификаторы static и unsafe.

  • Разделяемые типы могут быть универсальными. Ограничения помещаются в ту часть обяъвления разделяемого метода, где находится определение, и могут дополнительно повторяться в разделе реализации. Имена параметров и типов параметров необязательно должны совпадать в объявлении реализации и в объявлении определения.

  • Можно использовать делегат в качестве определенного и реализованного разделяемого метода, но его нельзя использовать в качестве разделяемого метода, который только определен.

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

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

См. также

Ссылки

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

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

Интерфейсы (Руководство по программированию в C#)

разделяемый (тип) (Справочник по C#)

Основные понятия

Руководство по программированию на C#