Шаг 73.
Язык программирования C#. Начала
Статические методы. Методы с произвольным количеством аргументов

    На этом шаге мы рассмотрим особенности реализации таких методов.

    Допустим, мы хотим описать метод, который вычисляет сумму переданных ему аргументов. В принципе, путем перегрузки метода мы можем описать версию с одним аргументом, двумя аргументами, тремя аргументами и так далее. Проблема в том, что на каждое количество аргументов нужна "персональная" версия метода. И сколько бы версий метода мы ни описали, всегда можно передать на один аргумент больше. Здесь мы сталкиваемся с проблемой, которая состоит в том, что наперед не известно, сколько аргументов будет передано методу при вызове. Причем мы хотим, чтобы можно было передавать любое количество. Именно для подобных случаев в С# есть механизм, позволяющий описывать методы с произвольным количеством аргументов. Набор аргументов, количество которых неизвестно, формально описывается как массив, но только перед описанием этого массива в списке аргументов указывается ключевое слово params. В теле метода обрабатываются такие аргументы, как элементы массива. Но при вызове метода аргументы просто перечисляются через запятую. Например, если некоторый метод описан в формате

  метод(params int[] args), 
то это означает, что при вызове методу может быть передано произвольное количество целочисленных аргументов. В теле метода к этим аргументам обращаемся так, как если бы они были элементами массива args: то есть args[0] (первый аргумент), args[1] (второй аргумент) и так далее. Количество переданных аргументов можно определить с помощью свойства Length (выражение вида args.Length).


В принципе, вместо аргумента, описанного как массив с ключевым словом params, можно передавать обычный массив соответствующего типа.

    Метод можно описывать так, что в нем будут как "обычные" (явно описанные) аргументы, так и аргументы, количество которых наперед не задано. В таком случае "обычные" аргументы описываются сначала. Допустим, мы хотим описать метод, которому при вызове обязательно передаются один символьный аргумент и произвольное количество целочисленных аргументов. Такой метод описывается в формате

  (char symb, params int[] args). 
Нельзя описать метод, у которого несколько наборов аргументов, количество которых неизвестно. Например, не получится описать метод, которому передается произвольное количество символьных и целочисленных аргументов.


При вызове метода фактически переданные ему аргументы заносятся в память. Если известен объем памяти, занимаемый аргументами, и тип значений, то можно вычислить количество этих аргументов. Если, например, известно, что методу передается один символьный аргумент и произвольное количество целочисленных аргументов, то задача о "распределении памяти" решается однозначно - можно определить, какая память выделена под символьный аргумент, а то, что осталось, выделено под целочисленные аргументы. Зная, какой объем памяти выделяется для значения целочисленного типа, можно определить количество целочисленных аргументов и область памяти, выделенную для каждого из них. Но если бы было указано, что некоторая область памяти выделена для неизвестного количества символьных и целочисленных значений, то здесь могут быть разные варианты распределения памяти. Проще говоря, задача не имеет однозначного решения. Поэтому в методе может быть только один набор аргументов, количество которых не задано.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace pr73_1
{
    class Program
    {
        // Метод для вычисления суммы чисел: 
        static int sum(params int[] a){
            // Локальная переменная (значение суммы): 
            int res = 0;
            // Перебор аргументов метода: 
            for (int k=0; k < a.Length; k++) {
                // Прибавление слагаемого к сумме: 
                res += a[k];
            }
            // Результат метода: 
            return res;
        }

        // Метод для извлечения символов из текста: 
        static string getText(string t, params int[] a){
            // Начальное значение формируемой текстовой строки: 
            string res="";
            // Перебор аргументов метода: 
            for(int k=0; k < a.Length; k++){
                // Добавление символа в текст: 
                res += t[a[k]];
            }
            // Результат метода: 
            return res;
        }

        // Метод отображает значения аргументов: 
        static void show(int[] a, params char[] b) {
            // Количество элементов в числовом массиве: 
            Console.Write("Чисел " + a.Length + ": ");
            // Значения элементов в числовом массиве: 
            for(int k=0; k < a.Length-1; k++){
                Console.Write(a[k] + " ");
            }
            Console.WriteLine("и " + a[a.Length-1]);
            // Количество символьных аргументов:
            Console.Write("Символов " + b.Length + ": ");
            // Значения символьных аргументов: 
            for(int k=0; k < b.Length-1; k++){
                Console.Write(b[k] + " ");
            }
            Console.WriteLine("и " + b[b.Length-1]);
        }

        // Главный метод программы:
        static void Main()
        {
            // Примеры вызова методов.
            // Вычисление суммы чисел:
            Console.WriteLine("Сумма чисел: " + sum(1, 6, 9, 2, 4)); 
            Console.WriteLine("Сумма чисел: " + sum(5, 1, 2));
            // Формируется текст:
            Console.WriteLine(getText("Раз два три", 0, 10, 8, 1));
            Console.WriteLine(getText("Бревно", 3, 5, 1, 5, 4));
            // Отображаются аргументы:
            show(new int[] {1, 3, 5}, 'А', 'В', 'С', 'D', 'Е');
            show(new int[] {1, 3, 5, 7, 9}, 'А', 'В', 'С', 'D') ;
            // Задержка:
            Console.ReadLine();
        }
    }
}
Архив проекта можно взять здесь.

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


Рис.1. Результат выполнения программы

    В программе описан статический метод sum(), предназначенный для вычисления суммы чисел, переданных аргументами методу. Аргумент описан как

  params int[] а;     . 
В теле метода объявлена локальная переменная res с начальным нулевым значением. В конструкции цикла перебираются элементы массива а (но в реальности это перебор аргументов, переданных методу), и за каждый цикл командой
  res += a[k];
к текущему значению переменной res прибавляется значение очередного аргумента. После того как сумма аргументов вычислена и записана в переменную res, значение переменной возвращается результатом метода. Так, если вызвать метод командой
  sum(1, 6, 9, 2, 4);      , 
то значением будет число 22 (сумма чисел 1, 6, 9, 2 и 4). Значением выражения
  sum(5, 1, 2); 
является число 8 (сумма чисел 5, 1 и 2).

    Метод getText() предназначен для формирования одной текстовой строки на основе другой текстовой строки. Аргументом методу передается текст (первый аргумент) и произвольное количество целочисленных аргументов. Эти целочисленные аргументы определяют индексы символов в текстовой строке (первый аргумент), из которых следует сформировать строку-результат. Например, если метод вызывается командой

  getText("Раз два три", 0, 10, 8, 1);      , 
то значение вычисляется так: из строки "Раз два три" берем символы с индексами 0, 10, 8 и 1 и объединяем их в текстовую строку. Это слово "Рита". Если метод вызывается командой
  getText ("Бревно", 3, 5, 1, 5, 4);    , 
то результатом является текст "ворон": из текста "Бревно" следует взять символы с индексами 3, 5, 1, снова 5 и 4.

    Код метода getText() реализован несложно. Объявляется текстовая переменная res, начальное значение которой пустая текстовая строка. Затем запускается конструкция цикла, в которой перебираются целочисленные аргументы метода (отождествляются с элементами массива а). Командой

  res += t[а[k]]; 
к текущему значению текстовой переменной res дописывается символ из текста t (первый аргумент метода) с индексом а[k]. Строка res возвращается результатом метода.

    Метод show() выполняет незатейливую работу - он отображает значения переданных ему аргументов. Первым аргументом методу передается целочисленный массив а (обычный). После этого обязательного аргумента методу может передаваться произвольное количество символьных аргументов. Поэтому формальный второй аргумент b метода описан как массив с идентификатором params. Методом при вызове отображается такая информация:

    Для проверки работы метода использованы команды

  show(new int[] {1, 3, 5}, 'А', 'В', 'С', 'D', 'Е') 
и
  show(new int[] {1, 3, 5, 7, 9}, 'А', 'В', 'С', 'D'). 
В обоих случаях первым аргументом методу передаются анонимные массивы, которые создаются командой вида
  new int[] {значения}.


Например, командой
  new int[] {1, 3, 5}; 
создается массив из трех элементов со значениями 1, 3 и 5. Значение выражения new int [] {1,3,5} - это ссылка на созданный массив. Эта ссылка передается первым аргументом методу show().

    На следующем шаге мы рассмотрим главный метод программы.




Предыдущий шаг Содержание Следующий шаг