Презентацию к данной лекции Вы можете скачать здесь.
Массив — это ограниченная совокупность однотипных величин. Элементы массива имеют одно и то же имя, а различаются по порядковому номеру ( индексу ).
Массив в С# относится к null для ссылочных.
На рис 6.1 представлен массив, состоящий из пяти элементов любого значимого типа, например, int или double, а рисунок 6.2 иллюстрирует организацию массива из элементов ссылочного типа.

(рис 6.2) Простые переменные и массив из элементов значимого типа(рис 6.1) Массив из элементов ссылочного типаВот, например, как выглядят операторы создания массива из 10 целых чисел и массива из 100 строк:
int[] w = new int[10]; string[] z = new string[100];
В первом операторе описан массив w типа int[]. Операция new выделяет память под 10 целых элементов, и они заполняются нулями.
Во втором операторе описан массив z типа string[].Операция new выделяет память под 100 ссылок на строки, и эти ссылки заполняются значением null. Память под сами строки, составляющие массив, не выделяется — это будет необходимо сделать перед заполнением массива.
Количество элементов в массиве ( размерность ) не является частью его типа, оно задается при выделении памяти и не может быть изменено впоследствии. Размерность может задаваться не только константой, но и выражением. Результат вычисления этого выражения должен быть неотрицательным, а его тип должен иметь неявное преобразование к int, uint, long или ulong.
Элементы массива нумеруются с нуля, поэтому максимальный номер элемента всегда на единицу меньше размерности. Для обращения к элементу массива после имени массива указывается номер элемента в квадратных скобках, например:
w[4] z[i]
С элементом массива можно делать все, что допустимо для переменных того же типа. Если при работе с массивом значение индекса выходит за границы массива, генерируется исключение IndexOutOfRangeException.
Массивы одного типа можно присваивать друг другу. При этом происходит присваивание ссылок, а не элементов, как и для любого другого объекта ссылочного типа, например:
int[] a = new int[10]; int[] b = a; // b и a указывают на один и тот же массив
Все массивы в C# имеют общий базовый класс Array, определенный в пространстве имен System. В нем есть несколько полезных методов, упрощающих работу с массивами, например, методы получения размерности, сортировки и поиска. Мы рассмотрим эти методы немного позже в разделе "Класс System.Array".
В C# существуют три разновидности массивов: одномерные, прямоугольные и ступенчатые (невыровненные). О последних, менее распространенных, можно прочитать в учебнике (Павловская Т. А. С#. Программирование на языке высокого уровня. — СПб.: Питер, 2010), а сейчас займемся одномерными массивами.
Одномерные массивы используются в программах чаще всего. Варианты описания массива:
тип[] имя;
тип[] имя = new тип [ размерность ];
тип[] имя = { список_инициализаторов };
тип[] имя = new тип [] { список_инициализаторов };
тип[] имя = new тип [ размерность ] { список_инициализаторов };
Примеры описаний (один пример на каждый вариант описания):
int[] a; // 1 элементов нет
int[] b = new int[4]; // 2 элементы равны 0
int[] c = { 61, 2, 5, -9 }; // 3 new подразумевается
int[] d = new int[] { 61, 2, 5, -9 }; // 4 размерность вычисляется
int[] e = new int[4] { 61, 2, 5, -9 }; // 5 избыточное описание
Здесь описано пять массивов. Отличие первого оператора от остальных состоит в том, что в нем фактически описана только ссылка на массив, а память под элементы массива не выделена.
В каждом из остальных массивов по четыре элемента целого типа. Как видно из операторов 3–5, массив при описании можно инициализировать. Если при этом не задана размерность (оператор 3), количество элементов вычисляется по количеству инициализирующих значений. Для полей объектов и локальных переменных можно опускать операцию new, она будет выполнена по умолчанию (оператор 2). Если присутствует и размерность, и список инициализаторов, размерность должна быть константой (оператор 4).
В качестве примера рассмотрим программу, которая определяет сумму и количество отрицательных элементов, а также максимальный элемент массива, состоящего из 6 целочисленных элементов (листинг 6.1).
using System;
namespace ConsoleApplication1
{ class Class1
{
static void Main()
{
const int n = 6;
int[] a = new int[n] { 3, 12, 5, -9, 8, -4 };
Console.WriteLine( "Исходный массив:" );
for ( int i = 0; i < n; ++i )
Console.Write( "\t" + a[i] );
Console.WriteLine();
long sum = 0; // cумма отрицательных элементов
int num = 0; // количество отрицательных элементов
for ( int i = 0; i < n; ++i )
if ( a[i] < 0 )
{
sum += a[i];
++num;
}
Console.WriteLine( "Сумма отрицательных = " + sum );
Console.WriteLine( "Кол-во отрицательных = " + num );
int max = a[0]; // максимальный элемент
for ( int i = 1; i < n; ++i )
if ( a[i] > max ) max = a[i];
Console.WriteLine( "Максимальный элемент = " + max );
}
}
}
Обратите внимание, что для вывода массива требуется организовать цикл.
Прямоугольный массив имеет более одного измерения. Чаще всего в программах используются двумерные массивы. Варианты описания двумерного массива:
тип[,] имя;
тип[,] имя = new тип [ разм_1, разм_2 ];
тип[,] имя = { список_инициализаторов };
тип[,] имя = new тип [,] { список_инициализаторов };
тип[,] имя = new тип [ разм_1, разм_2 ] { список_инициализаторов };
Примеры описаний (один пример на каждый вариант описания):
int[,] a; // 1 элементов нет
int[,] b = new int[2, 3]; // 2 элементы равны 0
int[,] c = {{1, 2, 3}, {4, 5, 6}}; // 3 new подразумевается
int[,] c = new int[,] {{1, 2, 3}, {4, 5, 6}}; // 4 размерность вычисляется
int[,] d = new int[2,3] {{1, 2, 3}, {4, 5, 6}};// 5 избыточное описание
К элементу двумерного массива обращаются, указывая номера строки и столбца, на пересечении которых он расположен, например:
a[1, 4] b[i, j] b[j, i]
Внимание
Необходимо помнить, что компилятор воспринимает как номер строки первый индекс, как бы он ни был обозначен в программе.
В качестве примера рассмотрим программу, которая для целочисленной матрицы размером 3 x 4 определяет среднее арифметическое ее элементов и количество положительных элементов в каждой строке.
Для нахождения среднего арифметического элементов массива требуется найти их общую сумму, после чего разделить ее на количество элементов. Порядок перебора элементов массива (по строкам или по столбцам) роли не играет. Нахождение количества положительных элементов каждой строки требует просмотра матрицы по строкам. Схема алгоритма приведена на рис 6.3, программа — в листинге 6.2.
using System;
namespace ConsoleApplication1
{ class Class1
{
static void Main()
{
const int m = 3, n = 4;
int[,] a = new int[m, n] {
{ 2,-2, 8, 9 },
{-4,-5, 6,-2 },
{ 7, 0, 1, 1 }
};
Console.WriteLine( "Исходный массив:" );
for ( int i = 0; i < m; ++i )
{
for ( int j = 0; j < n; ++j )
Console.Write( "\t" + a[i, j] );
Console.WriteLine();
}
double sum = 0;
int nPosEl;
for ( int i = 0; i < m; ++i )
{
nPosEl = 0;
for ( int j = 0; j < n; ++j )
{
sum += a[i, j];
if ( a[i, j] > 0 ) ++nPosEl;
}
Console.WriteLine( "В строке {0} {1} положит-х элементов",
i, nPosEl );
}
Console.WriteLine( "Среднее арифметическое всех элементов: "
+ sum / m / n );
}
}
}
В ступенчатых массивах количество элементов в разных строках может различаться. В памяти ступенчатый массив хранится иначе, чем прямоугольный: в виде нескольких внутренних массивов, каждый из которых имеет свой размер. Кроме того, выделяется отдельная область памяти для хранения ссылок на каждый из внутренних массивов (рис 6.3).
(рис 6.3) Ступенчатый массивФрагмент программы, иллюстрирующий работу со ступенчатым массивом, приведен в листинге 6.5 (после рассмотрения оператора foreach ).
Все массивы в C# построены на основе базового класса Array, который содержит полезные для программиста свойства и методы, часть из которых перечислена в таблилце 6.1.
| Элемент | Вид | Описание |
|---|---|---|
Length |
Свойство | Количество элементов массива (по всем размерностям) |
|
Статический метод | Двоичный поиск в отсортированном массиве |
Clear |
Статический метод | Присваивание элементам массива значений по умолчанию |
Copy |
Статический метод | Копирование заданного диапазона элементов одного массива в другой массив |
GetValue |
Метод | Получение значения элемента массива |
IndexOf |
Статический метод | Поиск первого вхождения элемента в одномерный массив |
Reverse |
Статический метод | Изменение порядка следования элементов на обратный |
Sort |
Статический метод | Упорядочивание элементов одномерного массива |
В листинге 6.3 продемонстрировано применение элементов класса Array при работе с одномерным массивом.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
int[] a = { 24, 50, 18, 3, 16, -7, 9, -1 };
PrintArray( "Исходный массив:", a );
Console.WriteLine( Array.IndexOf( a, 18 ) );
Array.Sort(a);
PrintArray( "Упорядоченный массив:", a );
Console.WriteLine( Array.BinarySearch( a, 18) );
}
public static void PrintArray( string header, int[] a )
{
Console.WriteLine( header );
for ( int i = 0; i < a.Length; ++i )
Console.Write( "\t" + a[i] );
Console.WriteLine();
}
}
}
Методы Sort, IndexOf и являются статическими, поэтому к ним обращаются через имя класса, а не экземпляра, и передают в них имя массива. Двоичный поиск можно применять только для упорядоченных массивов. В классе Class1 описан вспомогательный статический метод PrintArray, предназначенный для вывода массива на экран.
Результат работы программы:
Исходный массив:
24 50 18 3 16 -7 9 -1
2
Упорядоченный массив:
-7 -1 3 9 16 18 24 50
5
Оператор foreach применяется для перебора элементов в специальным образом организованной группе данных. Массив является именно такой группой. Удобство этого вида цикла заключается в том, что нам не требуется определять количество элементов в группе и выполнять их перебор по индексу: мы просто указываем на необходимость перебрать все элементы группы. Синтаксис оператора:
foreach ( тип имя in выражение ) тело_цикла
Имя задает локальную по отношению к циклу переменную, которая будет по очереди принимать все значения из массива выражение (в качестве выражения чаще всего применяется имя массива или другой группы данных). В простом или составном операторе, представляющем собой тело цикла, выполняются действия с переменной цикла. Тип переменной должен соответствовать типу элемента массива.
Например, пусть задан массив:
int[] a = { 24, 50, 18, 3, 16, -7, 9, -1 };
Вывод этого массива на экран с помощью оператора foreach выглядит следующим образом:
foreach ( int x in a ) Console.WriteLine( x );
Этот оператор выполняется так: на каждом проходе цикла очередной элемент массива присваивается переменной х и с ней производятся действия, записанные в теле цикла.
В листинге 6.4 решается та же задача, что и в листинге 6.1, но с использованием цикла foreach. Обратите внимание, насколько понятнее стала программа.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
int[] a = { 3, 12, 5, -9, 8, -4 };
Console.WriteLine( "Исходный массив:" );
foreach ( int elem in a )
Console.Write( "\t" + elem );
Console.WriteLine();
long sum = 0; // cумма отрицательных элементов
int num = 0; // количество отрицательных элементов
foreach ( int elem in a )
if ( elem < 0 )
{
sum += elem;
++num;
}
Console.WriteLine( "sum = " + sum );
Console.WriteLine( "num = " + num );
int max = a[0]; // максимальный элемент
foreach ( int elem in a )
if ( elem > max ) max = elem;
Console.WriteLine( "max = " + max );
}
}
}
Внимание
Ограничением оператора foreach является то, что с его помощью можно только просматривать значения в группе данных, но не изменять их.
Еще один пример использования оператора для работы со ступенчатым массивом приведен в листинге 6.5.
...
int[][] a = new int[3][];
a[0] = new int [5] { 24, 50, 18, 3, 16 };
a[1] = new int [3] { 7, 9, -1 };
a[2] = new int [4] { 6, 15, 3, 1 };
Console.WriteLine( "Исходный массив:" );
foreach ( int [] mas1 in a )
{
foreach ( int x in mas1 )
Console.Write( "\t" + x );
Console.WriteLine();
}
// поиск числа 18 в нулевой строке:
Console.WriteLine( Array.IndexOf( a[0], 18 ) );
...
При создании массива, состоящего из элементов ссылочного типа, память выделяется только под ссылки на элементы, а сами элементы необходимо разместить в хипе явным образом. В качестве примера создадим массив из объектов некоторого класса Monster:
using System;
namespace ConsoleApplication1
{
class Monster { ... }
class Class1
{ static void Main()
{
Random rnd = new Random();
const int n = 5;
Monster[] stado = new Monster[n]; // 1
for ( int i = 0; i < n; ++i ) // 2
{
stado[i] = new Monster( rnd.Next( 1, 100 ),
rnd.Next( 1, 200 ),
"Crazy" + i.ToString() );
}
foreach ( Monster x in stado ) x.Passport(); // 3
}
}
}
Результат работы программы:
Monster Crazy0 health = 18 ammo = 94 Monster Crazy1 health = 85 ammo = 75 Monster Crazy2 health = 13 ammo = 6 Monster Crazy3 health = 51 ammo = 104 Monster Crazy4 health = 68 ammo = 114
Пример поиска в массиве объектов приведен в конце следующей лекции.
Обработка текстовой информации является, вероятно, одной из самых распространенных задач в современном программировании, и C# предоставляет для ее решения широкий набор средств: отдельные символы, массивы символов, изменяемые и неизменяемые строки и регулярные выражения.
Символьный тип char предназначен для хранения символов в кодировке Unicode. Символьный тип относится к встроенным типам данных C# и соответствует стандартному классу Char библиотеки .NET из пространства имен System. В этом классе определены статические методы, позволяющие задать вид и категорию символа, а также преобразовать символ в верхний или нижний регистр и в число. Некоторые методы приведены в таблилце 6.2, с остальными можно ознакомиться по учебнику [4].
| Метод | Описание |
|---|---|
GetNumericValue |
Возвращает числовое значение символа, если он является цифрой, и –1 в противном случае |
IsControl |
Возвращает true, если символ является управляющим |
IsDigit |
Возвращает true, если символ является десятичной цифрой |
IsLetter |
Возвращает true, если символ является буквой |
IsLower |
Возвращает true, если символ задан в нижнем регистре |
IsUpper |
Возвращает true, если символ записан в верхнем регистре |
IsWhiteSpace |
Возвращает true, если символ является пробельным (пробел, перевод строки и возврат каретки) |
Parse |
Преобразует строку в символ (строка должна состоять из одного символа) |
ToLower |
Преобразует символ в нижний регистр |
, |
Возвращают символы с максимальным и минимальным кодами (эти символы не имеют видимого представления) |
В листинге 6.6 продемонстрировано использование этих методов.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
try
{
char b = 'B', c = '\x63', d = '\u0032'; // 1
Console.WriteLine( "{0} {1} {2}", b, c, d );
Console.WriteLine( "{0} {1} {2}",
char.ToLower(b), char.ToUpper(c), char.GetNumericValue(d) );
char a;
do // 2
{
Console.Write( "Введите символ: " );
a = char.Parse( Console.ReadLine() );
Console.WriteLine( "Введен символ {0}, его код – {1}",
a, (int)a );
if (char.IsLetter(a)) Console.WriteLine("Буква");
if (char.IsUpper(a)) Console.WriteLine("Верхний рег.");
if (char.IsLower(a)) Console.WriteLine("Нижний рег.");
if (char.IsControl(a)) Console.WriteLine("Управляющий");
if (char.IsNumber(a)) Console.WriteLine("Число");
if (char.IsPunctuation(a)) Console.WriteLine("Разделитель");
} while (a != 'q');
}
catch
{
Console.WriteLine( "Возникло исключение" );
return;
}
}
}
}
Массив символов, как и массив любого иного типа, построен на основе базового класса Array. Применение этих методов позволяет эффективно решать некоторые задачи. Простой пример приведен в листинге 6.7.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
char[] a = { 'm', 'a', 's', 's', 'i', 'v' }; // 1
char[] b = "а роза упала на лапу азора".ToCharArray(); // 2
PrintArray( "Исходный массив а:", a );
int pos = Array.IndexOf( a, 'm' );
a[pos] = 'M';
PrintArray( "Измененный массив а:", a );
PrintArray( "Исходный массив b:", b );
Array.Reverse( b );
PrintArray( "Измененный массив b:", b );
}
public static void PrintArray( string header, Array a )
{
Console.WriteLine( header );
foreach ( object x in a ) Console.Write( x );
Console.WriteLine( "\n" );
}
}
}
Результат работы программы:
Исходный массив а: massiv Измененный массив а: Massiv Исходный массив b: а роза упала на лапу азора Измененный массив b: ароза упал ан алапу азор а
Тип string, предназначенный для работы со строками символов в кодировке Unicode, является встроенным типом C#. Ему соответствует базовый класс System.String библиотеки .NET.
Создать строку можно несколькими способами:
string s; // инициализация отложена
string t = "qqq"; // инициализация строковым литералом
string u = new string(' ', 20); // конструктор создает строку из 20 пробелов
char[] a = { '0', '0', '0' }; // массив для инициализации строки
string v = new string( a ); // создание из массива символов
Для строк определены следующие операции:
= );== );!= );[] );+ ).Несмотря на то, что строки являются
Обращаться к отдельному элементу строки по индексу можно только для получения значения, но не для его изменения. Это связано с тем, что строки типа string относятся к так называемым неизменяемым типам данных. Методы, изменяющие содержимое строки, на самом деле создают новую копию строки. Неиспользуемые "старые" копии автоматически удаляются сборщиком мусора.
В классе System.String предусмотрено множество методов, полей и свойств, позволяющих выполнять со строками практически любые действия. Некоторые элементы класса приведены в таблилце 6.3, с остальными можно ознакомиться по учебнику.
| Название | Вид | Описание |
|---|---|---|
Compare |
Статический метод | Сравнение двух строк в лексикографическом (алфавитном) порядке. Разные реализации метода позволяют сравнивать строки и подстроки с учетом и без учета регистра и особенностей национального представления дат и т. д. |
Concat |
Статический метод | Конкатенация строк. Метод допускает сцепление произвольного числа строк |
Copy |
Статический метод | Создание копии строки |
Format |
Статический метод | Форматирование в соответствии с заданными спецификаторами формата (см. далее) |
IndexOf, IndexOfAny, , LastIndexOfAny |
Методы | Определение индексов первого и последнего вхождения заданной подстроки или любого символа из заданного набора |
Insert |
Метод | Вставка подстроки в заданную позицию |
Join |
Статический метод | Слияние массива строк в единую строку. Между элементами массива вставляются разделители (см. далее) |
Length |
Свойство | Длина строки (количество символов) |
Split |
Метод | Разделение строки на элементы, используя заданные разделители. Результаты помещаются в массив строк |
Substring |
Метод | |
ToCharArray |
Метод | Преобразование строки в массив символов |
ToLower, ToUpper |
Методы | Преобразование символов строки к нижнему или верхнему регистру |
Пример применения методов приведен в листинге 6.8.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
string s = "прекрасная королева Изольда";
Console.WriteLine( s );
string sub = s.Substring( 3 ).Remove( 12, 2 ); // 1
Console.WriteLine( sub );
string[] mas = s.Split(' '); // 2
string joined = string.Join( "! ", mas );
Console.WriteLine( joined );
Console.WriteLine( "Введите строку" );
string x = Console.ReadLine(); // 3
Console.WriteLine( "Вы ввели строку " + x );
double a = 12.234;
int b = 29;
Console.WriteLine( " a = {0,6:C} b = {1,2:X}", a, b ); // 4
Console.WriteLine( " a = {0,6:0.##} a = {1,5:0.# ' руб. '}",
a, b ); // 5
}
}
}
Результат работы программы:
прекрасная королева Изольда красная корова Изольда прекрасная! королева! Изольда Введите строку не хочу! Вы ввели строку не хочу! a = 12,23p. b = 1D a = 12,23 a=29 pуб.
В операторе 4 из листинга 6.7 неявно применяется метод Format, который заменяет все вхождения параметров в фигурных скобках значениями соответствующих переменных из списка вывода. После номера параметра можно задать минимальную ширину поля вывода, а также указать спецификатор формата, который определяет форму представления выводимого значения.
В общем виде параметр задается следующим образом:
{n [,m[:спецификатор_формата]]}
Здесь n — номер параметра. Параметры нумеруются с нуля, нулевой параметр заменяется значением первой переменной из списка вывода, первый параметр — второй переменной, и т. д. Параметр m определяет минимальную ширину поля, которое отводится под выводимое значение. Если выводимому числу достаточно меньшего количества позиций, неиспользуемые позиции заполняются пробелами. Если числу требуется больше позиций, параметр игнорируется.
Спецификатор формата, как явствует из его названия, определяет формат вывода значения. Например, спецификатор C (Currency) означает, что параметр должен форматироваться как валюта с учетом национальных особенностей представления, а спецификатор Х (
В операторе 5 используются так называемые пользовательские шаблоны форматирования. Если приглядеться, в них нет ничего сложного: после двоеточия задается вид выводимого значения посимвольно, причем на месте каждого символа может стоять либо #, либо 0. Если указан знак #, на этом месте будет выведена цифра числа, если она не равна нулю. Если указан 0, будет выведена любая цифра, в том числе и 0.
Возможности, предоставляемые классом string, широки, однако требование неизменности его объектов может оказаться неудобным. В этом случае для работы со строками применяется класс StringBuilder, определенный в пространстве имен System.Text и позволяющий изменять значение своих экземпляров. О нем можно прочитать в учебнике [4].
В одномерном массиве, состоящем из n вещественных элементов, вычислить:
Упорядочить элементы массива по возрастанию.
Дана целочисленная прямоугольная матрица. Определить:
В файле находится текст, состоящий не более чем из 50 предложений. Перед выполнением индивидуального варианта задания необходимо считать содержимое этого файла в массив строк, предусмотрев обработку исключений.
В качестве результата выполнения работы вывести на консоль предложения, преобразованные в соответствии с вариантом задания. Каждое предложение начинать с новой строки.
Задание выполнить двумя способами: без использования элементов стандартных классов System.Array, System.Char и System.String и с их использованием.
Задание: упорядочить предложения по возрастанию количества содержащихся в них слов.
Презентацию к данной лекции Вы можете скачать здесь.
Массив — это ограниченная совокупность однотипных величин. Элементы массива имеют одно и то же имя, а различаются по порядковому номеру ( индексу ).
Массив в С# относится к null для ссылочных.
На рис 6.1 представлен массив, состоящий из пяти элементов любого значимого типа, например, int или double, а рисунок 6.2 иллюстрирует организацию массива из элементов ссылочного типа.

(рис 6.2) Простые переменные и массив из элементов значимого типа(рис 6.1) Массив из элементов ссылочного типаВот, например, как выглядят операторы создания массива из 10 целых чисел и массива из 100 строк:
int[] w = new int[10]; string[] z = new string[100];
В первом операторе описан массив w типа int[]. Операция new выделяет память под 10 целых элементов, и они заполняются нулями.
Во втором операторе описан массив z типа string[].Операция new выделяет память под 100 ссылок на строки, и эти ссылки заполняются значением null. Память под сами строки, составляющие массив, не выделяется — это будет необходимо сделать перед заполнением массива.
Количество элементов в массиве ( размерность ) не является частью его типа, оно задается при выделении памяти и не может быть изменено впоследствии. Размерность может задаваться не только константой, но и выражением. Результат вычисления этого выражения должен быть неотрицательным, а его тип должен иметь неявное преобразование к int, uint, long или ulong.
Элементы массива нумеруются с нуля, поэтому максимальный номер элемента всегда на единицу меньше размерности. Для обращения к элементу массива после имени массива указывается номер элемента в квадратных скобках, например:
w[4] z[i]
С элементом массива можно делать все, что допустимо для переменных того же типа. Если при работе с массивом значение индекса выходит за границы массива, генерируется исключение IndexOutOfRangeException.
Массивы одного типа можно присваивать друг другу. При этом происходит присваивание ссылок, а не элементов, как и для любого другого объекта ссылочного типа, например:
int[] a = new int[10]; int[] b = a; // b и a указывают на один и тот же массив
Все массивы в C# имеют общий базовый класс Array, определенный в пространстве имен System. В нем есть несколько полезных методов, упрощающих работу с массивами, например, методы получения размерности, сортировки и поиска. Мы рассмотрим эти методы немного позже в разделе "Класс System.Array".
В C# существуют три разновидности массивов: одномерные, прямоугольные и ступенчатые (невыровненные). О последних, менее распространенных, можно прочитать в учебнике (Павловская Т. А. С#. Программирование на языке высокого уровня. — СПб.: Питер, 2010), а сейчас займемся одномерными массивами.
Одномерные массивы используются в программах чаще всего. Варианты описания массива:
тип[] имя;
тип[] имя = new тип [ размерность ];
тип[] имя = { список_инициализаторов };
тип[] имя = new тип [] { список_инициализаторов };
тип[] имя = new тип [ размерность ] { список_инициализаторов };
Примеры описаний (один пример на каждый вариант описания):
int[] a; // 1 элементов нет
int[] b = new int[4]; // 2 элементы равны 0
int[] c = { 61, 2, 5, -9 }; // 3 new подразумевается
int[] d = new int[] { 61, 2, 5, -9 }; // 4 размерность вычисляется
int[] e = new int[4] { 61, 2, 5, -9 }; // 5 избыточное описание
Здесь описано пять массивов. Отличие первого оператора от остальных состоит в том, что в нем фактически описана только ссылка на массив, а память под элементы массива не выделена.
В каждом из остальных массивов по четыре элемента целого типа. Как видно из операторов 3–5, массив при описании можно инициализировать. Если при этом не задана размерность (оператор 3), количество элементов вычисляется по количеству инициализирующих значений. Для полей объектов и локальных переменных можно опускать операцию new, она будет выполнена по умолчанию (оператор 2). Если присутствует и размерность, и список инициализаторов, размерность должна быть константой (оператор 4).
В качестве примера рассмотрим программу, которая определяет сумму и количество отрицательных элементов, а также максимальный элемент массива, состоящего из 6 целочисленных элементов (листинг 6.1).
using System;
namespace ConsoleApplication1
{ class Class1
{
static void Main()
{
const int n = 6;
int[] a = new int[n] { 3, 12, 5, -9, 8, -4 };
Console.WriteLine( "Исходный массив:" );
for ( int i = 0; i < n; ++i )
Console.Write( "\t" + a[i] );
Console.WriteLine();
long sum = 0; // cумма отрицательных элементов
int num = 0; // количество отрицательных элементов
for ( int i = 0; i < n; ++i )
if ( a[i] < 0 )
{
sum += a[i];
++num;
}
Console.WriteLine( "Сумма отрицательных = " + sum );
Console.WriteLine( "Кол-во отрицательных = " + num );
int max = a[0]; // максимальный элемент
for ( int i = 1; i < n; ++i )
if ( a[i] > max ) max = a[i];
Console.WriteLine( "Максимальный элемент = " + max );
}
}
}
Обратите внимание, что для вывода массива требуется организовать цикл.
Прямоугольный массив имеет более одного измерения. Чаще всего в программах используются двумерные массивы. Варианты описания двумерного массива:
тип[,] имя;
тип[,] имя = new тип [ разм_1, разм_2 ];
тип[,] имя = { список_инициализаторов };
тип[,] имя = new тип [,] { список_инициализаторов };
тип[,] имя = new тип [ разм_1, разм_2 ] { список_инициализаторов };
Примеры описаний (один пример на каждый вариант описания):
int[,] a; // 1 элементов нет
int[,] b = new int[2, 3]; // 2 элементы равны 0
int[,] c = {{1, 2, 3}, {4, 5, 6}}; // 3 new подразумевается
int[,] c = new int[,] {{1, 2, 3}, {4, 5, 6}}; // 4 размерность вычисляется
int[,] d = new int[2,3] {{1, 2, 3}, {4, 5, 6}};// 5 избыточное описание
К элементу двумерного массива обращаются, указывая номера строки и столбца, на пересечении которых он расположен, например:
a[1, 4] b[i, j] b[j, i]
Внимание
Необходимо помнить, что компилятор воспринимает как номер строки первый индекс, как бы он ни был обозначен в программе.
В качестве примера рассмотрим программу, которая для целочисленной матрицы размером 3 x 4 определяет среднее арифметическое ее элементов и количество положительных элементов в каждой строке.
Для нахождения среднего арифметического элементов массива требуется найти их общую сумму, после чего разделить ее на количество элементов. Порядок перебора элементов массива (по строкам или по столбцам) роли не играет. Нахождение количества положительных элементов каждой строки требует просмотра матрицы по строкам. Схема алгоритма приведена на рис 6.3, программа — в листинге 6.2.
using System;
namespace ConsoleApplication1
{ class Class1
{
static void Main()
{
const int m = 3, n = 4;
int[,] a = new int[m, n] {
{ 2,-2, 8, 9 },
{-4,-5, 6,-2 },
{ 7, 0, 1, 1 }
};
Console.WriteLine( "Исходный массив:" );
for ( int i = 0; i < m; ++i )
{
for ( int j = 0; j < n; ++j )
Console.Write( "\t" + a[i, j] );
Console.WriteLine();
}
double sum = 0;
int nPosEl;
for ( int i = 0; i < m; ++i )
{
nPosEl = 0;
for ( int j = 0; j < n; ++j )
{
sum += a[i, j];
if ( a[i, j] > 0 ) ++nPosEl;
}
Console.WriteLine( "В строке {0} {1} положит-х элементов",
i, nPosEl );
}
Console.WriteLine( "Среднее арифметическое всех элементов: "
+ sum / m / n );
}
}
}
В ступенчатых массивах количество элементов в разных строках может различаться. В памяти ступенчатый массив хранится иначе, чем прямоугольный: в виде нескольких внутренних массивов, каждый из которых имеет свой размер. Кроме того, выделяется отдельная область памяти для хранения ссылок на каждый из внутренних массивов (рис 6.3).
(рис 6.3) Ступенчатый массивФрагмент программы, иллюстрирующий работу со ступенчатым массивом, приведен в листинге 6.5 (после рассмотрения оператора foreach ).
Все массивы в C# построены на основе базового класса Array, который содержит полезные для программиста свойства и методы, часть из которых перечислена в таблилце 6.1.
| Элемент | Вид | Описание |
|---|---|---|
Length |
Свойство | Количество элементов массива (по всем размерностям) |
|
Статический метод | Двоичный поиск в отсортированном массиве |
Clear |
Статический метод | Присваивание элементам массива значений по умолчанию |
Copy |
Статический метод | Копирование заданного диапазона элементов одного массива в другой массив |
GetValue |
Метод | Получение значения элемента массива |
IndexOf |
Статический метод | Поиск первого вхождения элемента в одномерный массив |
Reverse |
Статический метод | Изменение порядка следования элементов на обратный |
Sort |
Статический метод | Упорядочивание элементов одномерного массива |
В листинге 6.3 продемонстрировано применение элементов класса Array при работе с одномерным массивом.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
int[] a = { 24, 50, 18, 3, 16, -7, 9, -1 };
PrintArray( "Исходный массив:", a );
Console.WriteLine( Array.IndexOf( a, 18 ) );
Array.Sort(a);
PrintArray( "Упорядоченный массив:", a );
Console.WriteLine( Array.BinarySearch( a, 18) );
}
public static void PrintArray( string header, int[] a )
{
Console.WriteLine( header );
for ( int i = 0; i < a.Length; ++i )
Console.Write( "\t" + a[i] );
Console.WriteLine();
}
}
}
Методы Sort, IndexOf и являются статическими, поэтому к ним обращаются через имя класса, а не экземпляра, и передают в них имя массива. Двоичный поиск можно применять только для упорядоченных массивов. В классе Class1 описан вспомогательный статический метод PrintArray, предназначенный для вывода массива на экран.
Результат работы программы:
Исходный массив:
24 50 18 3 16 -7 9 -1
2
Упорядоченный массив:
-7 -1 3 9 16 18 24 50
5
Оператор foreach применяется для перебора элементов в специальным образом организованной группе данных. Массив является именно такой группой. Удобство этого вида цикла заключается в том, что нам не требуется определять количество элементов в группе и выполнять их перебор по индексу: мы просто указываем на необходимость перебрать все элементы группы. Синтаксис оператора:
foreach ( тип имя in выражение ) тело_цикла
Имя задает локальную по отношению к циклу переменную, которая будет по очереди принимать все значения из массива выражение (в качестве выражения чаще всего применяется имя массива или другой группы данных). В простом или составном операторе, представляющем собой тело цикла, выполняются действия с переменной цикла. Тип переменной должен соответствовать типу элемента массива.
Например, пусть задан массив:
int[] a = { 24, 50, 18, 3, 16, -7, 9, -1 };
Вывод этого массива на экран с помощью оператора foreach выглядит следующим образом:
foreach ( int x in a ) Console.WriteLine( x );
Этот оператор выполняется так: на каждом проходе цикла очередной элемент массива присваивается переменной х и с ней производятся действия, записанные в теле цикла.
В листинге 6.4 решается та же задача, что и в листинге 6.1, но с использованием цикла foreach. Обратите внимание, насколько понятнее стала программа.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
int[] a = { 3, 12, 5, -9, 8, -4 };
Console.WriteLine( "Исходный массив:" );
foreach ( int elem in a )
Console.Write( "\t" + elem );
Console.WriteLine();
long sum = 0; // cумма отрицательных элементов
int num = 0; // количество отрицательных элементов
foreach ( int elem in a )
if ( elem < 0 )
{
sum += elem;
++num;
}
Console.WriteLine( "sum = " + sum );
Console.WriteLine( "num = " + num );
int max = a[0]; // максимальный элемент
foreach ( int elem in a )
if ( elem > max ) max = elem;
Console.WriteLine( "max = " + max );
}
}
}
Внимание
Ограничением оператора foreach является то, что с его помощью можно только просматривать значения в группе данных, но не изменять их.
Еще один пример использования оператора для работы со ступенчатым массивом приведен в листинге 6.5.
...
int[][] a = new int[3][];
a[0] = new int [5] { 24, 50, 18, 3, 16 };
a[1] = new int [3] { 7, 9, -1 };
a[2] = new int [4] { 6, 15, 3, 1 };
Console.WriteLine( "Исходный массив:" );
foreach ( int [] mas1 in a )
{
foreach ( int x in mas1 )
Console.Write( "\t" + x );
Console.WriteLine();
}
// поиск числа 18 в нулевой строке:
Console.WriteLine( Array.IndexOf( a[0], 18 ) );
...
При создании массива, состоящего из элементов ссылочного типа, память выделяется только под ссылки на элементы, а сами элементы необходимо разместить в хипе явным образом. В качестве примера создадим массив из объектов некоторого класса Monster:
using System;
namespace ConsoleApplication1
{
class Monster { ... }
class Class1
{ static void Main()
{
Random rnd = new Random();
const int n = 5;
Monster[] stado = new Monster[n]; // 1
for ( int i = 0; i < n; ++i ) // 2
{
stado[i] = new Monster( rnd.Next( 1, 100 ),
rnd.Next( 1, 200 ),
"Crazy" + i.ToString() );
}
foreach ( Monster x in stado ) x.Passport(); // 3
}
}
}
Результат работы программы:
Monster Crazy0 health = 18 ammo = 94 Monster Crazy1 health = 85 ammo = 75 Monster Crazy2 health = 13 ammo = 6 Monster Crazy3 health = 51 ammo = 104 Monster Crazy4 health = 68 ammo = 114
Пример поиска в массиве объектов приведен в конце следующей лекции.
Обработка текстовой информации является, вероятно, одной из самых распространенных задач в современном программировании, и C# предоставляет для ее решения широкий набор средств: отдельные символы, массивы символов, изменяемые и неизменяемые строки и регулярные выражения.
Символьный тип char предназначен для хранения символов в кодировке Unicode. Символьный тип относится к встроенным типам данных C# и соответствует стандартному классу Char библиотеки .NET из пространства имен System. В этом классе определены статические методы, позволяющие задать вид и категорию символа, а также преобразовать символ в верхний или нижний регистр и в число. Некоторые методы приведены в таблилце 6.2, с остальными можно ознакомиться по учебнику [4].
| Метод | Описание |
|---|---|
GetNumericValue |
Возвращает числовое значение символа, если он является цифрой, и –1 в противном случае |
IsControl |
Возвращает true, если символ является управляющим |
IsDigit |
Возвращает true, если символ является десятичной цифрой |
IsLetter |
Возвращает true, если символ является буквой |
IsLower |
Возвращает true, если символ задан в нижнем регистре |
IsUpper |
Возвращает true, если символ записан в верхнем регистре |
IsWhiteSpace |
Возвращает true, если символ является пробельным (пробел, перевод строки и возврат каретки) |
Parse |
Преобразует строку в символ (строка должна состоять из одного символа) |
ToLower |
Преобразует символ в нижний регистр |
, |
Возвращают символы с максимальным и минимальным кодами (эти символы не имеют видимого представления) |
В листинге 6.6 продемонстрировано использование этих методов.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
try
{
char b = 'B', c = '\x63', d = '\u0032'; // 1
Console.WriteLine( "{0} {1} {2}", b, c, d );
Console.WriteLine( "{0} {1} {2}",
char.ToLower(b), char.ToUpper(c), char.GetNumericValue(d) );
char a;
do // 2
{
Console.Write( "Введите символ: " );
a = char.Parse( Console.ReadLine() );
Console.WriteLine( "Введен символ {0}, его код – {1}",
a, (int)a );
if (char.IsLetter(a)) Console.WriteLine("Буква");
if (char.IsUpper(a)) Console.WriteLine("Верхний рег.");
if (char.IsLower(a)) Console.WriteLine("Нижний рег.");
if (char.IsControl(a)) Console.WriteLine("Управляющий");
if (char.IsNumber(a)) Console.WriteLine("Число");
if (char.IsPunctuation(a)) Console.WriteLine("Разделитель");
} while (a != 'q');
}
catch
{
Console.WriteLine( "Возникло исключение" );
return;
}
}
}
}
Массив символов, как и массив любого иного типа, построен на основе базового класса Array. Применение этих методов позволяет эффективно решать некоторые задачи. Простой пример приведен в листинге 6.7.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
char[] a = { 'm', 'a', 's', 's', 'i', 'v' }; // 1
char[] b = "а роза упала на лапу азора".ToCharArray(); // 2
PrintArray( "Исходный массив а:", a );
int pos = Array.IndexOf( a, 'm' );
a[pos] = 'M';
PrintArray( "Измененный массив а:", a );
PrintArray( "Исходный массив b:", b );
Array.Reverse( b );
PrintArray( "Измененный массив b:", b );
}
public static void PrintArray( string header, Array a )
{
Console.WriteLine( header );
foreach ( object x in a ) Console.Write( x );
Console.WriteLine( "\n" );
}
}
}
Результат работы программы:
Исходный массив а: massiv Измененный массив а: Massiv Исходный массив b: а роза упала на лапу азора Измененный массив b: ароза упал ан алапу азор а
Тип string, предназначенный для работы со строками символов в кодировке Unicode, является встроенным типом C#. Ему соответствует базовый класс System.String библиотеки .NET.
Создать строку можно несколькими способами:
string s; // инициализация отложена
string t = "qqq"; // инициализация строковым литералом
string u = new string(' ', 20); // конструктор создает строку из 20 пробелов
char[] a = { '0', '0', '0' }; // массив для инициализации строки
string v = new string( a ); // создание из массива символов
Для строк определены следующие операции:
= );== );!= );[] );+ ).Несмотря на то, что строки являются
Обращаться к отдельному элементу строки по индексу можно только для получения значения, но не для его изменения. Это связано с тем, что строки типа string относятся к так называемым неизменяемым типам данных. Методы, изменяющие содержимое строки, на самом деле создают новую копию строки. Неиспользуемые "старые" копии автоматически удаляются сборщиком мусора.
В классе System.String предусмотрено множество методов, полей и свойств, позволяющих выполнять со строками практически любые действия. Некоторые элементы класса приведены в таблилце 6.3, с остальными можно ознакомиться по учебнику.
| Название | Вид | Описание |
|---|---|---|
Compare |
Статический метод | Сравнение двух строк в лексикографическом (алфавитном) порядке. Разные реализации метода позволяют сравнивать строки и подстроки с учетом и без учета регистра и особенностей национального представления дат и т. д. |
Concat |
Статический метод | Конкатенация строк. Метод допускает сцепление произвольного числа строк |
Copy |
Статический метод | Создание копии строки |
Format |
Статический метод | Форматирование в соответствии с заданными спецификаторами формата (см. далее) |
IndexOf, IndexOfAny, , LastIndexOfAny |
Методы | Определение индексов первого и последнего вхождения заданной подстроки или любого символа из заданного набора |
Insert |
Метод | Вставка подстроки в заданную позицию |
Join |
Статический метод | Слияние массива строк в единую строку. Между элементами массива вставляются разделители (см. далее) |
Length |
Свойство | Длина строки (количество символов) |
Split |
Метод | Разделение строки на элементы, используя заданные разделители. Результаты помещаются в массив строк |
Substring |
Метод | |
ToCharArray |
Метод | Преобразование строки в массив символов |
ToLower, ToUpper |
Методы | Преобразование символов строки к нижнему или верхнему регистру |
Пример применения методов приведен в листинге 6.8.
using System;
namespace ConsoleApplication1
{ class Class1
{ static void Main()
{
string s = "прекрасная королева Изольда";
Console.WriteLine( s );
string sub = s.Substring( 3 ).Remove( 12, 2 ); // 1
Console.WriteLine( sub );
string[] mas = s.Split(' '); // 2
string joined = string.Join( "! ", mas );
Console.WriteLine( joined );
Console.WriteLine( "Введите строку" );
string x = Console.ReadLine(); // 3
Console.WriteLine( "Вы ввели строку " + x );
double a = 12.234;
int b = 29;
Console.WriteLine( " a = {0,6:C} b = {1,2:X}", a, b ); // 4
Console.WriteLine( " a = {0,6:0.##} a = {1,5:0.# ' руб. '}",
a, b ); // 5
}
}
}
Результат работы программы:
прекрасная королева Изольда красная корова Изольда прекрасная! королева! Изольда Введите строку не хочу! Вы ввели строку не хочу! a = 12,23p. b = 1D a = 12,23 a=29 pуб.
В операторе 4 из листинга 6.7 неявно применяется метод Format, который заменяет все вхождения параметров в фигурных скобках значениями соответствующих переменных из списка вывода. После номера параметра можно задать минимальную ширину поля вывода, а также указать спецификатор формата, который определяет форму представления выводимого значения.
В общем виде параметр задается следующим образом:
{n [,m[:спецификатор_формата]]}
Здесь n — номер параметра. Параметры нумеруются с нуля, нулевой параметр заменяется значением первой переменной из списка вывода, первый параметр — второй переменной, и т. д. Параметр m определяет минимальную ширину поля, которое отводится под выводимое значение. Если выводимому числу достаточно меньшего количества позиций, неиспользуемые позиции заполняются пробелами. Если числу требуется больше позиций, параметр игнорируется.
Спецификатор формата, как явствует из его названия, определяет формат вывода значения. Например, спецификатор C (Currency) означает, что параметр должен форматироваться как валюта с учетом национальных особенностей представления, а спецификатор Х (
В операторе 5 используются так называемые пользовательские шаблоны форматирования. Если приглядеться, в них нет ничего сложного: после двоеточия задается вид выводимого значения посимвольно, причем на месте каждого символа может стоять либо #, либо 0. Если указан знак #, на этом месте будет выведена цифра числа, если она не равна нулю. Если указан 0, будет выведена любая цифра, в том числе и 0.
Возможности, предоставляемые классом string, широки, однако требование неизменности его объектов может оказаться неудобным. В этом случае для работы со строками применяется класс StringBuilder, определенный в пространстве имен System.Text и позволяющий изменять значение своих экземпляров. О нем можно прочитать в учебнике [4].
В одномерном массиве, состоящем из n вещественных элементов, вычислить:
Упорядочить элементы массива по возрастанию.
Дана целочисленная прямоугольная матрица. Определить:
В файле находится текст, состоящий не более чем из 50 предложений. Перед выполнением индивидуального варианта задания необходимо считать содержимое этого файла в массив строк, предусмотрев обработку исключений.
В качестве результата выполнения работы вывести на консоль предложения, преобразованные в соответствии с вариантом задания. Каждое предложение начинать с новой строки.
Задание выполнить двумя способами: без использования элементов стандартных классов System.Array, System.Char и System.String и с их использованием.
Задание: упорядочить предложения по возрастанию количества содержащихся в них слов.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.