Асинхронный вызов метода - Передача параметров методу

ОГЛАВЛЕНИЕ

Передача параметров методу

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

private string FooWithParameters(string param1,
               int param2, ArrayList list)
{
    // изменить данные!
    param1 = "Modify Value for param1";
    param2 = 200;
    list = new ArrayList();

    return "Thank you for reading this article";
}

Вызывается FooWithParameters с использованием BeginInvoke и EndInvoke. Прежде чем что-то делать, надо объявить делегат, совпадающий с сигнатурой этого метода.

public delegate string DelegateWithParameters(string param1, 
                       int param2, ArrayList list);

BeginInvoke и EndInvoke словно разделяют функцию на два отдельных метода. BeginInvoke отвечает за прием всех входных параметров с последующими двумя дополнительными параметрами, которые есть у каждого BeginInvoke (делегат обратного вызова и объект состояния). EndInvoke отвечает за возврат всех выходных параметров (параметров, помеченных ref или out) и возвращаемого значения, при наличии такового. Пора выяснить, что в примере считается входными параметрами, а что – выходными. param1, param2 и list считаются входными параметрами, и поэтому они принимаются как аргументы для метода BeginInvoke. Возвращаемое значение типа string считается выходным параметром, поэтому оно будет возвращаемым типом для EndInvoke. Здорово, что компилятор способен сгенерировать правильную сигнатуру для BeginInvoke и EndInvoke на основе объявления делегата. Были изменены значения входных параметров, чтобы проверить, соответствует ли поведение тому, каким оно ожидается без вызова BeginInvoke и EndInvoke. Также перегруппируется ArrayList, передаваемый новому ArrayList. Попробуйте угадать, каким будет вывод.

private void CallFooWithParameters()
{
    // создать параметры для передачи функции
    string strParam1 = "Param1";
    int intValue = 100;
    ArrayList list = new ArrayList();
    list.Add("Item1");

    // создать делегат
    DelegateWithParameters delFoo =
        new DelegateWithParameters(FooWithParameters);

    // вызвать функцию BeginInvoke!
    IAsyncResult tag =
        delFoo.BeginInvoke(strParam1, intValue, list, null, null);

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

    // вызов end invoke для получения возвращаемого значения
    string strResult = delFoo.EndInvoke(tag);

    // вывести параметры на экран:
    Trace.WriteLine("param1: " + strParam1);
    Trace.WriteLine("param2: " + intValue);
    Trace.WriteLine("ArrayList count: " + list.Count);
}

Снова посмотрите на FooWithParameters.

private string FooWithParameters(string param1,
        int param2, ArrayList list)
{
    // изменить данные
    param1 = "Modify Value for param1";
    param2 = 200;
    list = new ArrayList();

    return "Thank you for reading this article";
}

Ниже показаны три строки из окна вывода после вызова EndInvoke():

param1: Param1
param2: 100
ArrayList count: 1

Надо разобраться во всем этом. Даже когда функция изменяет значения входных параметров, эти изменения не видны после вызова EndInvoke. Строка является изменяемым типом, поэтому создается копия строки, и изменение не передается обратно вызывающему оператору. Целые числа являются значимыми типами, и они создают копию при передаче по значению. Наконец, воссоздание ArrayList не возвращается вызывающему оператору, потому что ссылка на ArrayList передается по значению, и воссоздание ArrayList является созданием нового выделения для ArrayList с присвоением переданной "скопированной" ссылки. Ссылка теряется и обычно считается утечкой памяти; но, к счастью, сборщик мусора .NET в итоге уберет ее. Что если надо вернуть новый выделенный ArrayList и остальные изменения, внесенные в параметры? Что надо сделать? Следует пометить ArrayList как параметр ref. Еще добавляются выходные параметры, чтобы показать, как меняется EndInvoke.

private string FooWithOutAndRefParameters(string param1,
        out int param2, ref ArrayList list)
{
    // изменить данные
    param1 = "Modify Value for param1";
    param2 = 200;
    list = new ArrayList();

    return "Thank you for reading this article";
}

Надо рассмотреть, что считается выходным параметром, а что считается входным параметром.
•    Param1 – входной параметр, принимается только в BeginInvoke.
•    Param2 – входной и выходной; следовательно, он передается в BeginInvoke и в EndInvoke (EndInvoke вернет обновленное значение).
•    list передается по ссылке, и поэтому тоже передается в BeginInvoke и EndInvoke.

Теперь делегат выглядит так:

public delegate string DelegateWithOutAndRefParameters(string param1, 
                out int param2, ref ArrayList list);

и наконец, надо посмотреть на функцию, вызывающую FooWithOutAndRefParameters:

private void CallFooWithOutAndRefParameters()
{
    // создать параметры для передачи функции
    string strParam1 = "Param1";
    int intValue = 100;
    ArrayList list = new ArrayList();
    list.Add("Item1");

    // создать делегат
    DelegateWithOutAndRefParameters delFoo =
      new DelegateWithOutAndRefParameters(FooWithOutAndRefParameters);

    // вызвать функцию beginInvoke!
    IAsyncResult tag =
        delFoo.BeginInvoke(strParam1,
            out intValue,
            ref list,
            null, null);

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

    // вызов end invoke. intValue и list передаются
    // как аргументы, потому что они могут измениться внутри функции.
    string strResult =
        delFoo.EndInvoke(out intValue, ref list, tag);

    // вывести параметры на экран:
    Trace.WriteLine("param1: " + strParam1);
    Trace.WriteLine("param2: " + intValue);
    Trace.WriteLine("ArrayList count: " + list.Count);
    Trace.WriteLine("return value: " + strResult);
}

Вывод ниже:

param1: Param1
param2: 200
ArrayList count: 0
return value: Thank you for reading this article

param1 не меняется. Это входной параметр, и param2 был передан как выходной параметр и был изменен на 200. Список массивов был перераспределен и теперь указывает на новую ссылку из нуля элементов (оригинальная ссылка утеряна). Теперь понятно, как передаются параметры посредством BeginInvoke и EndInvoke. Пора рассмотреть уведомление о завершении неблокирующей функции.