Чтение онлайн

ЖАНРЫ

C# для профессионалов. Том II

Ватсон Карли

Шрифт:

fixed (int *рХ = Mine.X, *рХ2 = Mine2.X) {

Объявление массивов в стеке

C# предоставляет оператор

stackalloc
, который используется в соединении с указателями для объявления массива в стеке без накладных расходов. Массив, размещаемый таким образом, не является полным объектом
System.Array
в стиле C#, он является просто массивом чисел, аналогичным одномерному массиву C++. Элементы этого массива не инициализируются и доступны с помощью такого же синтаксиса, как и в C++, с использованием квадратных скобок для указателя.

Оператор

stackalloc
требует спецификации типа данных и числа размещаемых элементов.

Синтаксис C++:

unsigned long рМуArray[20];

Синтаксис C#:

ulong *pMyArray = stackalloc ulong[20];

Отметим, однако, что хотя эти массивы похожи, версия C# позволяет определить размер во время выполнения:

int X;

// инициализировать X

ulong *pMyArray = stackalloc ulong[X];

Интерфейсы

Интерфейсы являются особенностью C#, которая не имеет аналога в ANSI C++, хотя компания Microsoft ввела интерфейсы в C++ с помощью специального ключевого слова. Идея интерфейса развилась из интерфейсов COM, которые предназначены служить контрактом, который указывает, какие методы или свойства реализует объект.

Интерфейс в C# не совсем такой, как интерфейс COM, так как он не имеет связанного с ним GUID, не является производным из

IUnknown
и не имеет связанных с ним записей в реестре (хотя можно отобразить интерфейс C# на интерфейс COM). Интерфейс C# является просто множеством определений функций и свойств. Он может рассматриваться как аналог абстрактного класса и определяется с помощью синтаксиса аналогичного класса.

interface IMyInterface {

 void MyMethod(int X);

}

Можно заметить, однако, следующие синтаксические различия с определением класса:

□ Методы не имеют модификаторов доступа.

□ Методы никогда не реализуются в интерфейсе.

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

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

class MyClass : MyBaseClass, IMyInterface, IAnotherInterface // и т.д.

{

 public virtual void MyMethod(int X) {

// реализация

 }

 // и т.д.

В этом примере выбрана реализация

MyMethod
как виртуального метода с открытым доступом.

Интерфейсы могут также выводиться из других интерфейсов, и в этом случае производный интерфейс содержит свои собственные методы, а также методы базового интерфейса.

interface IMyInterface : IBaseInterface

Можно проверить, что объект реализует интерфейс, используя либо оператор

is
, либо оператор
as
для преобразования его в этот интерфейс. Альтернативно можно преобразовать его напрямую, но в этом случае будет получено исключение, если объект не реализует интерфейс, поэтому этот подход годится только в том случае, если известно, что преобразование пройдет успешно. Можно использовать полученную таким образом ссылку
на интерфейс, чтобы вызывать методы на этом интерфейсе (реализация будет предоставляться экземпляром класса).

IMyInterface MyInterface;

MyClass Mine = new MyClass;

MyInterface = Mine as IMyInterface;

if (MyInterface != null) MyInterface.MyMethod(10);

Основные применения интерфейсов следующие:

□ Взаимодействовать и устанавливать обратную совместимость с компонентами COM.

□ Служить в качестве контракта для других классов .NET. Интерфейс может использоваться для указания, что класс реализует некоторые свойства. Например, цикл C# foreach

работает
внутренне, проверяя, что класс, в котором он используется, реализует интерфейс
IEnumerate
, и вызывая затем методы, определенные этим интерфейсом.

Делегаты

Делегаты в C# не имеют прямого эквивалента в C++ и выполняют ту же самую задачу, что и указатели на функции в C++. Идея делегата состоит в том, что указатель на метод помещается в специальный класс вместе со ссылкой на объект, на котором вызывается метод (для метода экземпляра или со ссылкой

null
для статического метода). Это означает, что в отличие от указателя на функцию в C++, делегат C# содержит достаточно информации для вызова метода экземпляра.

Формально делегат является классом, который выводится из класса

System.Delegate
. Следовательно, создание экземпляра делегата включает два этапа: определение этого производного класса и объявление переменной соответствующего типа. Определение класса делегата включает данные полной сигнатуры (с возвращаемым типом) метода, который содержит делегат.

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

// определить класс делегата, который представляет метод,

// получающий int и возвращающий void

delegate void MyOp(int X);

Затем, для целей этого примера объявим класс, который содержит вызываемый метод:

// затем определение класса

class MyClass {

 void MyMethod(int X) {

// и т.д.

 }

}

Еще позже, может быть при реализации некоторого другого класса, имеется метод, которому должна быть передана ссылка на метод с помощью делегата:

void MethodThatTakesDelegate(MyOp Op) {

 // вызвать метод, передавая ему значение 4

 Oр(4);

}

// и т.д.

И, наконец, код, который реально использует делегата:

MyClass Mine = new MyClass;

// Создать экземпляр делегата MyOp. Настроить его,

// чтобы он указывал на метод MyMethod из Mine.

MyOp DoIt = new MyOp(Mine.MyMethod);

Поделиться с друзьями: