Передача массивов при помощи параметров ref и out 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Передача массивов при помощи параметров ref и out



 

Как и все параметры out, параметр out типа массива перед использованием необходимо присвоить; то есть, он должен быть присвоен вызываемым. Пример:

 

static void TestMethod1(out int[] arr)

{

arr = new int[10];

}

 

Как и все параметры ref, параметр ref типа массива должен быть определённым образом присвоен вызывающим. Таким образом, четкое присвоение вызываемым не требуется. Параметр ref типа массива можно изменять как результат вызова. Например, массиву можно присвоить значение null, или же его можно инициализировать в другой массив. Пример:

 

static void TestMethod1(ref int[] arr)

{

arr = new int[10]; // arr инициализируется в другой массив

}

 

Следующие два примера демонстрируют отличия между параметрами out и ref при использовании для подстановки массивов в методы:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

 

class Program

{

static void FillArray(out int[] arr)

{

arr = new int[5] { 1, 2, 3, 4, 5 };

}

 

static void Main()

{

int[] theArray; // Инициализация не требуется

// Передаем массив вызываемый использованием out

FillArray(out theArray);

Console.WriteLine("Массив элементов:");

for (int i = 0; i < theArray.Length; i++)

{

Console.Write(theArray[i] + " ");

}

Console.WriteLine("\nДля продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет:

* Массив элементов:

* 1 2 3 4 5

* Для продолжения нажмите любую клавишу...

*/

 

В этом примере массив theArray инициализирован в вызывающем (метод Main) и подставляется в метод FillArray при помощи параметра ref. Некоторые из элементов массива обновляются в методе FillArray. Затем элементы массива возвращаются вызывающему и отображаются.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

 

class Program

{

static void FillArray(ref int[] arr)

{

if (arr == null)

{

arr = new int[10];

}

arr[0] = 1111;

arr[4] = 5555;

}

 

static void Main()

{

int[] theArray = { 1, 2, 3, 4, 5 };

// ref

FillArray(ref theArray);

Console.WriteLine("Массив элементов:");

for (int i = 0; i < theArray.Length; i++)

{

Console.Write(theArray[i] + " ");

}

Console.WriteLine("\nДля продолжения нажмите любую клавишу... ");

Console.ReadKey();

}

}

}

/* Выведет:

* Массив элементов:

* 1111 2 3 4 5555

* Для продолжения нажмите любую клавишу...

*/

Класс «Array»

Класс «Array»

 

Класс «Array»

 

Каждый создаваемый массив в C# получает большую часть функциональности от класса System.Array. Общие члены этого класса позволяют работать с массивом с использованием полноценной объектной модели. Таким образом, методы и свойства, определённые в классе Array, можно использовать с любым массивом С#. Разберём некоторые полезные особенности класса Array.

 

Создание массивов

 

Класс Array является абстрактным, поэтому создать массив с использованием какого-либо конструктора нельзя. Однако вместо применения синтаксиса С# для создания экземпляров массивов также возможно создавать их с помощью статического метода CreateInstance. Это исключительно удобно, когда заранее не известен тип элементов массива, поскольку тип можно передать методу CreateInstance в параметре как объект Туре:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main(string[] args)

{

// Создаем массив типа string, длиной 5

Array myArr = Array.CreateInstance(typeof(string), 5);

// Инициализируем первые два поля массива

myArr.SetValue("Имя", 0);

myArr.SetValue("Возраст", 1);

// Считываем данные из массива

string s = (string)myArr.GetValue(1);

Console.WriteLine(s);

// Выведет: Возраст

Console.WriteLine("\nДля продолжения нажмите любую клавишу...");

Console.ReadLine();

}

}

}

 

Обратим внимание, что для установки значений в массив служит метод SetValue, а для их чтения — метод GetValue.

Копирование массивов

Поскольку массивы — это ссылочные типы, присваивание переменной типа массива другой переменной создает две переменных, ссылающихся на один и тот же массив. Для копирования массивов предусмотрена реализация массивами интерфейса ICloneable. Метод Clone, определённый в этом интерфейсе, создает неглубокую копию массива. Если элементы массива относятся к типу значений, то все они копируются, если массив содержит элементы ссылочных типов, то сами эти элементы не копируются, а копируются лишь ссылки на них.

Вместо метода Clone можно также применять метод Array.Сору, тоже создающий поверхностную копию. Но между Clone и Сору есть одно важное отличие: Clone создаёт новый массив, а Сору требует наличия существующего массива той же размерности с достаточным количеством элементов. Пример:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main(string[] args)

{

string[] arr2 = new string[5];

// Создаем массив типа string, длиной 5

Array myArr = Array.CreateInstance(typeof(string),5);

// Инициализируем первые три поля массива

myArr.SetValue("Имя", 0);

myArr.SetValue("Возраст", 1);

myArr.SetValue("Адрес", 2);

// Копируем массив с помощью метода "Clone"

string[] arr1 = (string[])myArr.Clone();

foreach (string s in arr1)

Console.Write("\t"+s);

// Копируем с помощью метода "Copy"

Console.WriteLine();

Array.Copy(myArr, arr2, myArr.Length);

foreach (string s in arr2)

Console.Write("\t"+s);

Console.WriteLine("\nДля продолжения нажмите любую клавишу... ");

Console.ReadLine();

}

}

}

/* Выведет:

* Имя Возраст Адрес

* Имя Возраст Адрес

* Для продолжения нажмите любую клавишу...

*/

 

Сортировка и поиск

В классе Array реализован алгоритм быстрой сортировки (Quicksort) элементов массива. Метод Sort требует от элементов реализации интерфейса IComparable. Простые типы, такие как System.String и System.Int32, реализуют IComparable, так что можно сортировать элементы, относящиеся к этим типам.

C помощью разных вариантов метода Sort можно отсортировать массив полностью или в заданных пределах либо отсортировать два массива, содержащих соответствующие пары «ключ-значение». После сортировки в массиве можно осуществить эффективный поиск, используя разные варианты метода BinarySearch. Пример:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main(string[] args)

{

int[] myArr = { 4, 5, -183, 12, 34, 0, 2, -13 };

Console.WriteLine("Исходный массив чисел: ");

foreach (int x in myArr)

Console.Write("\t{0}", x);

// Реализуем сортировку массива

Console.WriteLine("\n\nОтсортированный массив:");

Array.Sort(myArr);

foreach (int x in myArr)

Console.Write("\t{0}", x);

// Организуем поиск числа 12

Console.WriteLine("\n\nПоиск числа:");

int search = Array.BinarySearch(myArr, 12);

Console.WriteLine("Число 12 находится на {0} позиции", search + 1);

Console.WriteLine("Для продолжения нажмите любую клавишу... ");

Console.ReadLine();

}

}

}

 

Рис. 4. 1. Результат работы кода выше

Понятие «Кортеж»

Понятие «Кортеж»

 

Массивы комбинируют объекты одного типа, а кортежи (Tuple) могут комбинировать объекты различных типов. Понятие кортежей происходит из языков функционального программирования, таких как F#, где они часто используются. С появлением .NET Framework 4 кортежи стали доступны в среде .NET для всех языков.NET.

В среде.NET определены восемь обобщённых классов Tuple и один статический класс Tuple, который служит «фабрикой» кортежей.

Существуют различные обобщённые классы Tuple для поддержки различного количества элементов; например, Tuple<T1> содержит один элемент, Tuple<T1, Т2> — два элемента и прочее. Элементы кортежа могут быть доступны через свойства Item1, Item2, ..., ItemM.

 

В случае если имеется более восьми элементов, которые нужно включить в кортеж, можно использовать определение класса Tuple с восемью параметрами. Последний параметр называется TRest, в котором должен передаваться сам кортеж. Таким образом, есть возможность создавать кортежи с любым количеством параметров.

 

Рассмотрим пример использования кортежей:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

// Данный метод возвращает кортеж с 4-мя

// разными значениями

static Tuple<int, float, string, char> Corteg(int a, string name)

{

int sqr = a * a;

float sqrt = (float)(Math.Sqrt(a));

string s = "Привет, " + name;

char ch = (char)(name[0]);

 

return Tuple.Create<int, float, string, char>(sqr, sqrt, s, ch);

}

 

static void Main()

{

var myTuple = Corteg(25, "Кто-то");

Console.WriteLine("{0}\n25 в квадрате: {1}\nКвадратный корень из 25: "

+ "{2}\nПервый символ в имени: {3}", myTuple.Item3, myTuple.Item1, myTuple.Item2, myTuple.Item4);

// Создаём кортеж произвольной размерности

var myTuple2 = Tuple.Create<int, char, string, decimal, float, byte, short, Tuple<int,

float, string, char>>(12, 'C', "Имя", 12.3892m, 0.5f, 120, 4501, myTuple);

Console.WriteLine("\nВыводим элементы кортежа myTuple2:\n{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}", myTuple2.Item1, myTuple2.Item2, myTuple2.Item3, myTuple2.Item4, myTuple2.Item5, myTuple2.Item6, myTuple2.Item7);

Console.WriteLine("\nДля продолжения нажмите любую клавишу... ");

Console.ReadLine();

}

}

}

 

Рис. 1. Результат работы кода выше

Понятие «Строка»

Понятие «Строка»

 

Понятие «Строка»

 

С точки зрения регулярного программирования строковый тип данных string относится к числу самых важных в С#. Этот тип определяет и поддерживает символьные строки. В целом ряде других языков программирования строка представляет собой массив символов. А в С# строки являются объектами. Следовательно, тип string относится к числу ссылочных.

 

Построение строк

 

Самый простой способ построить символьную строку — воспользоваться строковым литералом. Например, в следующей строке кода переменной ссылки на строку str присваивается ссылка на строковый литерал:

 

string str = "Пример строки";

 

В данном случае переменная str инициализируется последовательностью символов "Пример строки". Объект типа string можно также создать из массива типа char. Например:

 

char[] chararray = { 'e', 'x', 'a', 'm', 'p', 'l', 'e' };

string str = new string(chararray);

 

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

Работа со строками

В System.String предоставляется набор методов для определения длины символьных данных, поиска подстроки в текущей строке, преобразования символов из верхнего регистра в нижний и наоборот, и т.д. В следующей таблице перечислены некоторые наиболее интересные члены этого класса:

 

Метод Назначение
Compare() Статический метод, который позволяет сравнить две строки
CompareOrdinal() То же, что метод Compare, но без учёта локальных установок
Concat() Комбинирует отдельные экземпляры строк в одну строку (конкатенация)
Contains() Метод, который позволяет определить, содержится ли в строке определенная подстрока
СоруТо() Копирует определенное число символов, начиная с определенной позиции в новый экземпляр массива
Equals() Метод, который позволяет проверить, содержатся ли в двух строковых объектах идентичные символьные данные
Format() Статический метод, позволяющий сформатировать строку с использованием других элементарных типов данных (например, числовых данных или других строк) и обозначений типа {0}
IndexOf() Находит первое вхождение заданной подстроки или символа в строке
IndexOfAny() Находит первое вхождение в строку любого символа из набора
Insert() Метод, который позволяет вставить строку внутрь другой определенной строки
Join() Строит новую строку, комбинируя содержимое массива строк
LastlndexOf() То же, что IndexOf, но находит последнее вхождение
LastlndexOfAny() То же, что IndexOfAny, но находит последнее вхождение
PadLeft() PadRight() Методы, которые позволяют дополнить строку какими-то символами, соответственно, справа или слева
Remove() Replace() Методы, которые позволяют получить копию строки с соответствующими изменениями (удалением или заменой символов)
Spilt() Метод, возвращающий массив string с присутствующими в данном экземпляре подстроками внутри, которые отделяются друг от друга элементами из указанного массива char или string
Substring() Извлекает подстроку, начиная с определенной позиции строки
ToUpper() ToLower() Методы, которые позволяют создавать копию текущей строки в формате, соответственно, верхнего или нижнего регистра
Trim() Метод, который позволяет удалять все вхождения определенного набора символов с начала и конца текущей строки

 

Пример следующей программы использует несколько из вышеуказанных методов:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main(string[] args)

{

// Сравним первые две строки

string s1 = "это строка";

string s2 = "это текст, а это строка";

if (String.CompareOrdinal(s1, s2)!= 0)

Console.WriteLine("Строки s1 и s2 не равны");

if (String.Compare(s1, 0, s2, 13, 10, true) == 0)

Console.WriteLine("При этом в них есть одинаковый текст");

// Конкатенация строк

Console.WriteLine(String.Concat("\n" + "Один, два ", "три, четыре"));

// Поиск в строке

// Первое вхождение подстроки

if (s2.IndexOf("это")!= -1)

Console.WriteLine("Слово \"это\" найдено в строке, оно " +

"находится на: {0} позиции", s1.IndexOf("это"));

// Последнее вхождение подстроки

if (s2.LastIndexOf("это")!= -1)

Console.WriteLine("Последнее вхождение слова \"это\" находится "

+ "на {0} позиции", s2.LastIndexOf("это"));

// Поиск из массива символов

char[] myCh = { 'Ы', 'х', 'т' };

if (s2.IndexOfAny(myCh)!= -1)

Console.WriteLine("Один из символов из массива ch " +

"найден в текущей строке на позиции {0}", s2.IndexOfAny(myCh));

// Определяем начинается ли строка с заданной подстроки

if (s2.StartsWith("это текст") == true)

Console.WriteLine("Подстрока найдена!");

// Определяем содержится ли в строке подстрока

// на примере определения ОС пользователя

string myOS = Environment.OSVersion.ToString();

if (myOS.Contains("NT 6.1"))

Console.WriteLine("Ваша операционная система Windows 7");

Console.WriteLine("\nДля продолжения нажмите любую клавишу...");;

Console.ReadKey();

}

}

}

 

Рис. 3. 1. Результат работы кода выше

 

Постоянство строк

 

Как ни странно, содержимое объекта типа string не подлежит изменению. Этo означает, что однажды созданную последовательность символов изменить нельзя. Но данное ограничение способствует более эффективной реализации символьных строк. Поэтому этот, на первый взгляд, очевидный недостаток на самом деле превращается в преимущество. Так, если требуется строка в качестве разновидности уже имеющейся строки, то для этой цели следует создать новую строку, содержащую все необходимые изменения. А поскольку неиспользуемые строковые объекты автоматически собираются в «мусор», то о дальнейшей судьбе ненужных строк можно даже не беспокоиться.

 

Следует, однако, подчеркнуть, что переменные ссылки на строки (т.е. объекты типа string) подлежат изменению, а следовательно, они могут ссылаться на другой объект. Но содержимое самого объекта типа string не меняется после его создания.

 

Рассмотрим пример:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void addNewString()

{

string s = "Начальная символьная строка";

s = "Новая символьная строка";

Console.WriteLine(s);

}

 

static void Main()

{

addNewString();

Console.WriteLine("\nДля продолжения нажмите любую клавишу...");;

Console.ReadKey();

}

}

}

 

Скомпилируем приложение и загрузим результирующую сборку в утилиту IL DASM («Дизассемблер IL», файл ildasm.exe). Утилиту можно найти по умолчанию по следующему пути (для Windows 7): Пуск -> Все программы -> Microsoft Visual Studio 2010 -> Microsoft Windows SDK Tools -> Дизассемблер IL. Главное окно утилиты выглядит так:

 

Рис. 4. 1. Окно утилиты IL DASM (ildasm.exe)

 

Открываем EXE-файл скомпилированного приложения из предыдущего кода консольного приложения. На рисунке показан CIL-код, который будет сгенерирован для метода addNewString:

 

Рис. 4. 2. IL DASM: разбор ассемблерных действий метода addNewString

 

Обратим внимание на наличие многочисленных вызовов кода операции ldstr (загрузка строки). Этот код операции ldstr в CIL предусматривает выполнение загрузки нового объекта string в управляемую кучу. В результате предыдущий объект, в котором содержалось значение «"This is my first stroke"», будет в конечном итоге удалено сборщиком мусора.

Класс «StringBuilder»

Класс «StringBuilder»

 

Когда строка в C# конструируется классом String, выделяется ровно столько памяти, сколько необходимо для хранения данной строки. Однако, в пространстве имён System.Text имеется класс StringBuilder, который поступает по-другому и обычно выделяет больше памяти, чем нужно в данный момент. У разработчика приложения, есть возможность указать, сколько именно памяти должен выделить StringBuilder, но если этого не сделать, то будет выбран объём по умолчанию, который зависит от размера начального текста, инициализирующего экземпляр StringBuilder. Класс StringBuilder имеет два главных свойства:

  • Length, показывающее длину строки, содержащуюся в объекте в данный момент
  • Capacity, указывающее максимальную длину строки, которая может поместиться в выделенную для объекта память.

 

Любые модификации строки происходят внутри блока памяти, выделенного экземпляру StringBuilder. Это делает добавление подстрок и замену индивидуальных символов строки очень эффективными. Удаление или вставка подстрок неизбежно остаются менее эффективными, потому что при этих операциях приходится перемещать в памяти части строки. Выделять новую память и, возможно, полностью перемещать ее содержимое приходится только при выполнении ряда действий, которые приводят к превышению выделенной емкости строки. В дополнение к избыточной памяти, выделяемой изначально на основе экспериментов, StringBuilder имеет свойство удваивать свою ёмкость, когда происходит переполнение, а новое значение емкости не установлено явно.

 

Рассмотрим следующий пример:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main()

{

StringBuilder strBuild = new StringBuilder("Это начальный текст в строке", 120);

strBuild.AppendFormat(". Этот текст добавлен при вызове метода AppendFormat");

Console.WriteLine(strBuild);

Console.WriteLine("Для продолжения нажмите любую клавишу...");;

Console.ReadKey();

}

}

}

 

В данном примере начальная ёмкость StringBuilder равна 120. Всегда лучше сразу указывать емкость, превышающую предполагаемую длину строки, чтобы объекту StringBuilder не приходилось заново выделять память при переполнении. По умолчанию устанавливается ёмкость в 16 символов. Схематически данный пример работает следующим образом:

Рис. 1. 1. Использование памяти экземпляром класса StringBuilder strBuild из кода выше

 

Т.е. при вызове метода AppendFormat остаток текста помещается в пустое пространство без необходимости перераспределения памяти. Однако реальная эффективность, которую несёт с собой применение StringBuilder, проявляется при выполнении повторных подстановок текста.

 

В следующей таблице перечислены основные методы класса StringBuilder:

 

Метод Назначение
Append() Добавляет строку к текущей строке
AppendFormat() Добавляет строку, сформированную в соответствии со спецификатором формата
Insert() Вставляет подстроку в строку
Remove() Удаляет символ из текущей строки
Replace() Заменяет все вхождения символа другим символом или вхождения подстроки другой подстрокой
ToString() Возвращает текущую строку в воде объекта System.String (переопределение метода класса System.Object)

 

Рассмотрим практический пример использования класса StringBuilder:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

class Program

{

static void Main()

{

StringBuilder strBuild = new StringBuilder("Это начальный текст в строке", 120);

strBuild.AppendFormat(". Этот текст добавлен при вызове метода AppendFormat");

Console.WriteLine(strBuild);

// Зашифруем строку, хранящуюся в переменной strBuild

Console.WriteLine("Исходная строка: \n {0}\n", strBuild);

for (int i = 'я'; i >= 'a'; i--)

strBuild = strBuild.Replace((char)i, (char)(i + 3));

Console.WriteLine("Зашифрованная строка:\n {0}", strBuild);

Console.WriteLine("\nДля продолжения нажмите любую клавишу...");;

Console.ReadKey();

}

}

}

 

Рис. 1. 2. Результат работы кода выше

Форматирующие строки

Форматирующие строки

 

Форматирующие строки

 

Если необходимо, чтобы разрабатываемые классы были дружественными к пользователю, они должны предлагать средства для отображения своих строковых представлений в любом из форматов, которые могут понадобиться пользователю. В исполняющей среде .NET определён стандартный способ достижения этого —интерфейс IFormattable.

Начнём с рассмотрения того, что происходит, когда форматная строка применяется к примитивному типу, а отсюда станет ясно, как следует включать спецификаторы формата для пользовательских классов и структур:

 

decimal d = 12.05667m;

int i = 5;

Console.WriteLine("Значине переменной d = {0:C}, а i = {1}", d, i);

 

Сама строка формата содержит большую часть отображаемого текста, но всякий раз, когда в неё должно быть вставлено значение переменной, в фигурных скобках указывается индекс. В фигурные скобки может быть включена и другая информация, относящаяся к формату данного элемента, например, та, что описана ниже:

  • Количество символов, которое займёт представление элемента, снабженное префиксом-запятой. Отрицательное число указывает, что элемент должен быть выровнен по левой границе, а положительное — по правой. Если элемент на самом деле занимает больше символов, чем ему отведено форматом, он отображается полностью.
  • Спецификатор формата предваряется двоеточием. Это указывает, каким образом необходимо отформатировать элемент. Например, можно указать, должно ли число быть форматировано как денежное значение, либо его следует отобразить в научной нотации.

 

В следующей таблице перечислены часто используемые спецификаторы формата для числовых типов:

 

Спецификатор Применяется к Значение Пример
C Числовым типам Символ местной валюты $835.50 (США) £835.50 (Великобритания) 835.50р.(Россия)
D Только к целочисленным типам Обычное целое  
E Числовым типам Экспоненциальная нотация 8.35Е+002
F Числовым типам С фиксированной десятичной точкой 835.50
G Числовым типам Обычные числа 835.5
N Числовым типам Формат чисел, принятый в данной местности 4,384.50 (Великобритания/США) 4384,50 (континентальная Европа)
P Числовым типам Процентная нотация 835,000.00%
X Только к целочисленным типам Шестнадцатеричный формат 1a1f

 

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

 



Поделиться:


Последнее изменение этой страницы: 2016-12-28; просмотров: 876; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.135.205.146 (0.201 с.)