Значения перечисления и операции
Каждый перечисляемый тип определяет отдельный тип; требуется явное преобразование перечисления (§6.2.2), чтобы выполнить преобразование между перечисляемым типом и целым типом, или между двумя перечисляемыми типами. Набор значений перечисляемого типа может включать его члены, но не ограничивается только ими. А именно, любое значение базового типа перечисления может быть приведено к перечисляемому типу, и оно является отдельным допустимым значением этого перечисляемого типа.
Члены перечисления имеют тип содержащего их перечисляемого типа (кроме случаев, когда они входят в инициализаторы других членов перечисления: см. §14.3). Значением члена перечисления, объявленного в перечисляемом типе E со связанным значением v, является (E)v.
Со значениями перечисляемых типов могут использоваться следующие операторы: ==, !=, <, >, <=, >= (§7.10.5), бинарный + (§7.8.4), бинарный‑ (§7.8.5), ^, &, | (§7.11.2), ~ (§7.7.4), ++ и -- (§7.6.9 и §7.7.5).
Каждый перечисляемыйтип автоматически наследуется от класса System.Enum (который, в свою очередь, наследуется от классов System.ValueType и object). Таким образом, унаследованные методы и свойства этого класса могут использоваться в значениях перечисляемого типа.
Делегаты
Делегаты делают возможными сценарии, которые в других языках, таких как C++, Pascal и Modula, реализуются с помощью указателей на функции. Однако в отличие от указателей на функции языка C++, делегаты являются полностью объектно-ориентированными, и в отличие от указателей на функции-члены языка C++, делегаты инкапсулируют и экземпляр объекта, и метод.
Объявление делегата определяет класс, производный от класса System.Delegate. Экземпляр делегата инкапсулирует список вызова, являющийся списком из одного или более методов, на каждый из которых ссылаются как на объект, допускающий вызов. Для методов экземпляров объект, допускающий вызов, состоит из экземпляра и метода для этого экземпляра. Для статических методов объект, допускающий вызов, состоит только из метода. Вызов экземпляра делегата с соответствующим набором аргументов приводит к вызову каждого из допускающих вызов объектов делегата с данным набором аргументов.
Интересным и полезным свойством экземпляра делегата является то, что он не знает и не заботится о классах методов, которые инкапсулирует; имеет значение только совместимость этих методов (§15.1) с типом делегата. Это делает делегаты вполне пригодными для «анонимного» вызова.
Объявления делегатов
Объявление_делегата — это объявление_типа (§9.6), которое объявляет новый тип делегата.
delegate-declaration:
attributesopt delegate-modifiersopt delegate return-type
identifier variant-type-parameter-listopt
( formal-parameter-listopt ) type-parameter-constraints-clausesopt ;
delegate-modifiers:
delegate-modifier
delegate-modifiers delegate-modifier
delegate-modifier:
new
public
protected
internal
private
Возникает ошибка времени компиляции, если один и тот же модификатор встречается несколько раз в объявлении делегата.
Модификатор new допускается только для делегатов, объявленных в другом типе; в этом случае модификатор указывает, что такой делегат скрывает унаследованный член с тем же именем, как описано в §10.3.4.
Модификаторы public, protected, internal и private управляют доступом к типу делегата. В зависимости от контекста, в котором встречается объявление делегата, некоторые из этих модификаторов запрещены (§3.5.1).
Именем типа делегата является идентификатор.
Необязательный список_формальных_параметров определяет параметры делегата, а тип_возвращаемого_значения указывает тип возвращаемого значения делегата.
Необязательный список_параметров_типа_варианта (§13.1.3) задает параметры типа для самого делегата.
Возвращаемый тип типа делегата должен быть void или безопасным при выводе (§13.1.3.1).
Все типы формальных параметров типа делегата должны быть безопасными при вводе. Кроме того, любой из типов параметров out или ref также должен быть безопасным при выводе. Обратите внимание, что даже параметры out должны быть безопасными при вводе, согласно ограничению базовой платформы выполнения.
Типы делегатов в C# являются эквивалентом имени, а не структурным эквивалентом. А именно, два разных типа делегатов с одинаковыми списками параметров и типом возвращаемого значения считаются разными типами делегатов. Однако экземпляры двух отдельных, но структурно эквивалентных типов делегатов могут сопоставляться как равные (§7.9.8).
Пример:
delegate int D1(int i, double d);
class A
{
public static int M1(int a, double b) {...}
}
class B
{
delegate int D2(int c, double d);
public static int M1(int f, double g) {...}
public static void M2(int k, double l) {...}
public static int M3(int g) {...}
public static void M4(int g) {...}
}
Методы A.M1 и B.M1 совместимы с типами делегатов D1 и D2, так как у них одинаковый тип возвращаемого значения и список параметров; однако эти типы делегатов являются двумя разными типами, так что они не являются взаимозаменяемыми. Методы B.M2, B.M3 и B.M4 несовместимы с типами делегатов D1 и D2, так как у них разные типы возвращаемого значения или списки параметров.
Подобно другим объявлениям универсальных типов, должны быть заданы аргументы типа для создания сконструированного типа делегата. Типы параметров и тип возвращаемого значения сконструированного типа делегата создаются заменой каждого параметра типа в объявлении делегата соответствующим аргументом типа сконструированного типа делегата. Результирующие тип возвращаемого значения и типы параметров используются при определении, какие методы совместимы со сконструированным типом делегата. Пример:
delegate bool Predicate<T>(T value);
class X
{
static bool F(int i) {...}
static bool G(string s) {...}
}
Метод X.F совместим с типом делегата Predicate<int>, а метод X.G совместим с типом делегата Predicate<string>.
Объявление_делегата — это единственный способ объявить тип делегата. Тип делегата является типом класса, производного от System.Delegate. Типы делегатов являются неявно sealed, поэтому никакие производные типы от типа делегата не допускаются. Недопустимо также наследовать от класса System.Delegate тип класса, отличного от класса делегата. Обратите внимание, что System.Delegate сам не является типом делегата; это тип класса, от которого производятся все типы делегатов.
Язык C# предоставляет специальный синтаксис для создания экземпляра и вызова делегата. За исключением создания экземпляра, любые операции, которые могут применяться к классу или экземпляру класса, могут также применяться к классу делегата или экземпляру делегата, соответственно. В частности, можно получить доступ к членам типа System.Delegate с помощью обычного синтаксиса доступа к члену.
Набор методов, инкапсулированных экземпляром делегата, называется списком вызова. Если экземпляр делегата создается (§15.2) из единственного метода, он инкапсулирует этот метод, и его список вызова содержит только одну запись. Однако, когда объединяются два непустых экземпляра делегата, их списки вызова связываются — в очередности: левый операнд, а затем правый операнд — для формирования нового списка вызова, содержащего две или более записей.
Делегаты объединяются с помощью бинарных операторов + (§7.8.4) и += (§7.17.2). Делегат можно удалить из объединения делегатов с помощью бинарных операторов - (§7.8.5) и -= (§7.17.2). Делегаты можно проверять на равенство (§7.10.8).
В следующем примере показано создание нескольких экземпляров делегата и их соответствующие списки вызова:
delegate void D(int x);
class C
{
public static void M1(int i) {...}
public static void M2(int i) {...}
}
class Test
{
static void Main() {
D cd1 = new D(C.M1); // M1
D cd2 = new D(C.M2); // M2
D cd3 = cd1 + cd2; // M1 + M2
D cd4 = cd3 + cd1; // M1 + M2 + M1
D cd5 = cd4 + cd3; // M1 + M2 + M1 + M1 + M2
}
}
При создании экземпляров cd1 и cd2 каждый из них инкапсулирует один метод. При создании экземпляра cd3 его список вызова содержит два метода, M1 и M2 в этом порядке. Список вызова экземпляра cd4 содержит методы M1, M2 и M1 в этом порядке. Список вызова экземпляра cd5 содержит методы M1, M2, M1, M1 и M2 в этом порядке. Дополнительные примеры объединения (а также удаления) делегатов см. в §15.4.
Совместимость делегатов
Метод или делегат M совместим с типом делегата D, если верны все следующие условия:
· D и M имеют одинаковое число параметров, и каждый параметр в D имеет такие же модификаторы ref или out, как и соответствующий параметр в M;
· для каждого параметра значения (параметр без модификатора ref или out) существует преобразование идентификации (§6.1.1) или неявное преобразование ссылки (§6.1.6) из типа параметра в D в соответствующий тип параметра в M;
· для каждого параметра ref или out тип параметра в D такой же, как тип параметра в M;
· существует преобразование идентификации или неявное преобразование ссылки из типа возвращаемого значения M в тип возвращаемого значения D.