Необходимость в создании последовательности случайных чисел возникает в программировании довольно часто. Кроме того, способ, которым это делается в C#, характерен с точки зрения объектно-ориентированного подхода. В других языках программирования для генерации случайных чисел имеется некоторая функция (например, Random в языке Pascal). Следует учитывать детерминированную природу алгоритма генерации случайных чисел. Обычно такие алгоритмы основаны на некотором перемешивании цифр начального числа («зерна»). Для того чтобы серия случайных чисел каждый раз была другой, в качестве начального зерна алгоритму следует передавать различные значения начального зерна. Для этого в языке Pascal имеется функция Randomize, использующая в качестве начального зерна текущее системное время.
Однако в C# нет самостоятельных функций, а только методы классов. В качестве ближайшей аналогии функциям Random и Randomize можно использовать статические методы некоторого класса. Однако на самом деле в C# имеется класс Random c набором методов для генерации случайных чисел. Таким образом, фрагмент программы, генерирующий два случайных числа, может выглядеть так:
Random rnd=new Random();
//целое случайное число в диапазоне от 1 до 6
int i=rnd.Next(1,7);
//целое вещественное число в диапазоне от 0 до1
double d=rnd.NextDouble();
Таким способом решается и проблема уникального начального зерна – при создании нового объекта в качестве начального зерна неявно используется текущее системное время. Тем не менее, класс Random следует использовать внимательно. Следующий фрагмент продемонстрирует две одинаковые серии случайных чисел – i11 будет равно i21, а i12 – равно i22.
Random rnd1=new Random();
Random rnd2=new Random();
int i11=rnd.Next(1,7);
int i12=rnd.Next(1,7);
int i21=rnd.Next(1,7);
int i22=rnd.Next(1,7);
Дело в том, что недостаточная точность измерения системного времени при создании объектов rnd1 и rnd2 привела к созданию идентичных объектов. Для создания различных объектов необходимо обеспечить достаточный интервал между их созданием.
Массивы в языке C#
Хотя основные приемы использования массивов C# унаследовал от C++, следует обратить внимание на ряд важных особенностей.
Каждый массив является объектом класса System.Array. Поэтому в жизненном цикле массива имеется стадия описания массива и стадия создания массива. Внимание – в описании массива не указывается размер (количество элементов):
int [] Arr1;
Person [] Arr2;
Как и раньше, массив – это коллекция однотипных элементов. Массив Arr1 будет содержать целые числа, а массив Arr2 – объекты класса Person. Еще отметим «перемещение» пары квадратных скобок – они в C# указываются перед именем массива. Таким образом, конструкция «int []» является полноценным описателем типа массива.
Далее массив можно создать и на этой стадии нам понадобится операция new:
Arr1 = new int[10];
Использование new почти не изменилось – после new нужно указать тип объекта, а у нас это int[]. Только теперь в квадратных скобках нужно указать количество элементов массива. Кроме того, отсутствуют скобки со списком параметров.
Дальнейшее использование массива может происходить обычным образомю Например:
for (int i=0; i<10; i++) Arr1[i] = i*2;
Как видите, нумерация, по прежнему начинается с 0.
Аналогично поступим с массивом Arr2:
Arr2 = new Person[Arr1[3]];
Здесь проявилась замечательная особенность массивов в C# - их размер может задаваться выражением, значение которого определится только во время выполнения программы. Более того, Вы можете заново создать массив:
Arr2 = new string[Arr2.Length + 2];
Здесь мы воспользовались свойством Length класса System.Array. В результате размер нового массива больше на 2 элемента размера старого. Но учтите, что «новый» массив не содержит элементов старого массива – ведь это совсем новый объект в новом месте памяти.
Отметим, что в примере мы не инициализировали массив. В отношении массива требование инициализации не действует. Дело в том, что при создании массива с помощью new создается множество ссылок, каждая из которых содержит «пустой» указатель null. Этого достаточно, чтобы C# позволил приступить к использованию массива. Однако здесь появляется возможность для ошибки во время выполнения массива – если Вы попытаетесь использовать объект, на который ссылается такая null-ссылка. Поэтому нужно выполнить что-то в таком духе:
for (int i=0; i<Arr2.Length; i++) Arr2[i]=new Person();
Поскольку массив – это объект специального типа, его можно использовать как параметр метода или как тип возвращаемого значения. Например, следующий метод ModifyArray принимает любой целочисленный массив в качестве параметра и возвращает целочисленный массив вдвое большего размера, первая половина которого заполнена данными массива-параметра, а вторая – нулями.
Заметим, что таким образом мы можем имитировать полную динамичность массива, как множества элементов – добавлять в него новые элементы уже после создания массива или удалять существующие. Однако более эффективня реализация таких гибких структур данных достигается с помощью других контейнерных классов, оо которых Вы узнаете дальше.
Следует отметить, что кроме обычных приемов работы с массивами (обращение к элементам с помощью индекса, циклы и т.д.) класс System.Array предоставляет ряд дополнительных и весьма полезных методов. Некоторые из них перечислены в следующей таблице:
Метод
Описание
static int IndexOf
(Array array, Object value)
Возвращает первое вхождение значения value в массив array. Если array не содержит заданного значения, метод возвращает отрицательное целое число.
public static void Sort
(Array array)
Сортирует элементы во всем одномерном массиве array.
static int BinarySearch
(Array array, Object value)
Быстрый поиск методом половинного деления позиции значения value в объекте array. Перед вызовом этого метода объект array необходимо отсортировать. Если array не содержит заданного значения, метод возвращает отрицательное целое число.