Московский государственный университет имени М.В.Ломоносова
Опубликован: 01.11.2004 | Доступ: свободный | Студентов: 11271 / 455 | Оценка: 4.12 / 4.01 | Длительность: 19:20:00
ISBN: 978-5-9556-0077-9
Специальности: Программист
Лекция 18:

C#. Отражения. Делегаты

< Лекция 17 || Лекция 18: 123 || Лекция 19 >

Применение делегатов как методов обратного вызова

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

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

Например:

using System;
namespace MyDelegatе1
{
class Class1
{[STAThread]
  static void Main(string[] args)  {
    CA var1= new CA();
// Создание экземпляра делегата
  CA.Metod1Callback myCall =
  new CA.Metod1Callback(Class1.Metod2);  // Значение 
      // параметра, передаваемое методу обратного 
      // вызова Class1.Metod2, определено 
      // в методе Metod1 как "1".

// Выполнение метода обратного вызова (Metod2), 
// переданного в качестве параметра
    CA.Metod1(myCall);
}
static void Metod2 (string str2){
  Console.WriteLine("Выполняется метод Metod2");
  Console.WriteLine(str2);
  }
}
}
using System;
namespace MyDelegatе1
{
public class CA
{ public CA() {  }
  public delegate void Metod1Callback(string str1);
  public static void Metod1(Metod1Callback callback)
  {
    Console.WriteLine("Выполняется метод Metod1");
    // Параметр callback используется для вызова 
    // метода обратного вызова и передачи ему 
     // параметра (строки "1")
  callback("1");    
  }
}
}
Листинг 18.1.

В результате выполнения этого приложения сначала будет вызван метод Metod1, а затем Metod2.

Применение неуправляемого кода

По умолчанию приложения на C# относятся к управляемому коду. Но при необходимости управляемый код может взаимодействовать с неуправляемым кодом. К неуправляемому коду, вызываемому из управляемых C# приложений, можно отнести функции DLL-библиотек и сервисы COM-компонентов. Приложение управляемого кода на языке C# также может включать фрагменты небезопасного кода. Небезопасный код тоже относится к неуправляемому коду, так как выделение и освобождение памяти в нем не контролируется исполняющей средой .NET.

Небезопасный код

Фрагмент небезопасного кода следует помечать ключевым словом unsafe.

Например:

int i1;
unsafe {int *i2=&i1;}

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

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

Например:

using System;
class Class1 
{
  unsafe static void M1 (int* p) 
  // Небезопасный код: указатель на int
  {   *p *= *p;   }  // *p - доступ к значению
  unsafe public static void Main() 
  // Небезопасный код: применен оператор 
  // получения адреса (&)
  {  int i = 4;
    M1 (&i);
    Console.WriteLine (i);
  }
}

Чтобы использовать небезопасный код, следует установить опцию компилятора /unsafe. Для этого достаточно выбрать имя проекта в окне Solution Explorer и через контекстное меню вызвать диалог Property Pages (рис. 18.1) , а затем на странице Configuration Properties | Build установить значение опции Allow unsafe code blocks равным True.

Диалог Property Pages

Рис. 18.1. Диалог Property Pages

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

Выполняющая среда .NET для эффективного управления памятью при удалении одних объектов "перемещает" другие объекты, чтобы исключить фрагментацию памяти свободными блоками памяти. Таким образом, выполняющая среда .NET по умолчанию не гарантирует, что объект, на который получен указатель, всегда будет иметь один и тот же адрес. Для предотвращения перемещения объекта следует использовать оператор fixed, который имеет следующее формальное описание:

fixed ( тип* имя_указателя = выражение ) 
                  выполняемый_оператор_или_блок

В качестве типа можно указывать любой неуправляемый тип или void. Выражение является указателем на заданный тип. Фиксация объекта применяется только для указанного выполняемого оператора или блока. Доступ к фиксированной переменной не ограничен областью видимости небезопасного кода. Поэтому фиксированная переменная может указывать на значение, располагаемое в более широкой области видимости, чем данная область видимости небезопасного кода. При этом компилятор C# не выдает предупреждений о такой ситуации.

Однако компилятор C# не позволит установить указатель на управляемую переменную вне оператора fixed.

Например:

class Point 
    { public int x, y; }
class Class1
{[STAThread]
  static void Main(string[] args)
    {
      unsafe
  {
  Point pt = new Point();    // pt - это управляемая 
                    // переменная
  fixed ( int* p = &pt.x ) {  *p = 1 }    // pt.x -
                    // указывает на значение 
                    // размерного типа
  }
}  }

Одним оператором fixed можно фиксировать несколько указателей, но только в том случае, если они одного типа.

Например:

fixed (byte* pa1 = array1, pa2 = array2) {...}

В том случае, если требуется зафиксировать указатели различных типов, можно использовать вложенные операторы fixed.

Например:

fixed (int* p1 = &p.x)
  fixed (double* p2 = &array1[5]) {    }

Указатели, которые инициализированы оператором fixed, не могут быть изменены. Если объект, на который устанавливается указатель, размещается в стеке (например, переменная типа int), то его местоположение фиксировать не требуется.

Разместить блок памяти в стеке можно и явным образом, используя оператор stackalloc, который имеет следующее формальное описание:

тип * имя_указателя = stackalloc тип [ выражение ];

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

Например:

using System; 
class Class1
{public static unsafe void Main() 
  {
    int* a1 = stackalloc int[100];
    int* p = a1;      // Указатель на первый
                  // элемент массива
    *p++ = *p++ = 1;
    for (int i=2; i<100; ++i, ++p)
        *p = p[-1] + p[-2];
    for (int i=0; i<10; ++i) Console.WriteLine (a1[i]);
  }
}

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

< Лекция 17 || Лекция 18: 123 || Лекция 19 >
Александр Демьяненко
Александр Демьяненко

Можно ли сдавать один и тот же тест несколько раз?
Или же один и тот же тест можно сдать лишь однажды?

Максим Стогний
Максим Стогний

Добрый день!

Скажите, пожалуйста, если в терминологии объектно-ориентированного программирования функции также называются методами или методами - членами класса, в примере объявления указателя на метод использовали в формальном описании оба названия:

тип_метода (имя_класса::*имя_метода_указателя)
    (список параметров);
тип_функции (*имя_ функции_указателя)
    (список параметров);

при этом можно было  тип_функции во втором описании заменить на тип_метода? Т.е.:

тип_метода (*имя_ метода_указателя)
    (список параметров);