Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Массивы в качестве параметров




Массивы могут передаваться в методы в качестве параметров, а также возвращаться из методов. Для возврата массива достаточно объявить массив как тип возврата, как показано в следующем методе GetPersons():

static Person[] GetPersons()

{

return new Person [ ] {

new Person { FirstName="Damon", LastName="Hill" },

new Person { FirstName="Niki", LastName="Lauda" },

new Person { FirstName="Ayrton", LastName="Senna" },

new Person { FirstName="Graham", LastName="Hill" }

};

}

Для передачи массивов в метод массив объявляется в параметре, как показано в следую­щем методе DisplayPersons():

static void DisplayPersons(Person[] persons)

{

//...

Ковариантость массивов

Для массивов поддерживается ковариантность. Это значит, что массив может быть объ­явлен как базовый тип, и его элементам могут присваиваться элементы производных ти­пов. Например, можно объявить параметр типа object[] и передать в нем Person[]:

static void DisplayArray(object[] data).

{

//...

}

Ковариантность массивов возможна только, для ссылочных типов, но не для ти­пов значений.

 

С ковариантностью массивов связана проблема, которая может быть решена толь­ко через исключения времени выполнения. Если присвоить массив Person массиву object, mo массив object затем может быть использован со всем, что наследует­ся от object. Например, компилятор разрешит передавать строки в элементах такого массива. Однако, поскольку на самом деле ссылка на массив Person производится через массив object, возникает исключение времени выполнения.

Структура ArraySegment<T>

Структура ArraySegment<T>представляет сегмент массива. Это структура может при­меняться, когда нужно вернуть или передать методу части массива. Вместо передачи в ме­тод массива, смещения и счетчика в отдельных параметрах, можно передать единствен­ный параметр ArraySegment<T>.В этой структуре информация о сегменте (смещение и счетчик) заключена непосредственно в ее членах.

Метод SumOfSegmentsпринимает массив элементов ArraySegment<int>для вычисле­ния суммы всех целых чисел, определенных в сегменте, и возвращает эту сумму:

static int SumOfSegments(ArraySegment<int>[] segments)

{

int sum =0;

foreach (var segment in segments)

{

for (int i=segment.Offset; i<segment.Offset+segment.Count; i++)

{

sum += segment.Array[i];

}

}

return sum;

}

Этот метод используется посредством передачи массива сегментов. Первый элемент массива ссылается на три элемента ar1, начиная с первого, а второй элемент - на три элемента аr2, начиная с четвертого:

int[] ar1 = { 1, 4, 5, 11, 13, 18 };

int[] ar2 = { 3, 4, 5, 18, 21, 27, 33 };

var segments = new ArraySegment<int>[2]

{

new ArraySegment<int>(ar1, 0, 3),

new ArraySegment<int>(ar2, 3, 3)

};

var sum = SumOfSegments(segments);

 

Важно отметить, что сегменты массива не копируют элементы исходного мас­сива. Вместо этого через ArraySegment<T> можно получить доступ к исходно­му массиву Если изменяются элементы сегмента, то эти изменения будут видны в исходном массиве.

Перечисления

С помощью оператора foreachможно выполнять итерацию по элементам коллекции (см. раздел 6.10) без необходимости знания количества ее элементов. Оператор foreach использует перечислитель (enumerator). На рис. 6.7 показано отношение между клиен­том, вызывающим foreach, и коллекцией. Массив или коллекция реализует интерфейс IEnumerableс методом GetEnumerator(). Метод GetEnumerator() возвращает перечис­литель, реализующий интерфейс IEnumerable. Интерфейс IEnumerableзатем применя­ется оператором foreachдля выполнения итерации по элементам коллекции.

 

 

Метод GetEnumerator() определен в интерфейсе IEnumerable. Оператор foreach в действительности не нуждается в там, чтобы класс коллекции реали­зовывал этот интерфейс. Достаточно иметь метод по имени GetEnumerator(), который возвращает объект, реализующий интерфейс IEnumerator.

 


Рисунок 6.7 - Отношение между клиентом, вызывающим foreach, и коллекцией

Интерфейс IEnumerator

Оператор foreach использует методы и свойства интерфейса IEnumerator для итера­ции по всем элементам коллекции. Для этого IEnumerator определяет свойство Current для возврата элемента, на котором позиционирован курсор, и метод MoveNext() возвра­щает true, если есть элемент, и false, если доступных элементов больше нет.

Обобщенная версия этого интерфейса IEnumerator<T> - унаследована от интер­фейса IDisposable, и потому определяет метод Dispose() для очистки ресурсов, выде­ленных для перечислителя.

Интерфейс lEnumerator также определяет метод Reset() для возможно­сти взаимодействия с СОМ. Реализация этого метода во многих перечислите­лях.NETсводится к генерации исключения типа NotSupportedException.

Оператор foreach

Оператор foreach в C# не преобразуется к оператору foreach в IL. Вместо этого ком­пилятор C# преобразует оператор foreach в методы и свойства интерфейса IEnumerable. Ниже приведен простой пример оператора foreach для итерации по всем элементам мас­сива персон и отображения их друг за другом:

foreach (var р in persons)

{

Console.WriteLine(р);

}

Оператор foreach преобразуется в следующий сегмент кода. Сначала вызывается ме­тод GetEnumerator() для получения перечислителя для массива. Внутри цикла while - до тех пор, пока MoveNext() возвращает true - элементы массива доступны через свойство Current:

IEnumerator<Person> enumerator = persons.GetEnumerator();

while (enumerator.MoveNext())

{

Person p = (Person)enumerator.Current;

Console.WriteLine(p);

}

Оператор yield

Со времени первого выпуска C# позволяет легко выполнять итерации по коллекциям с помощью оператора foreach. В C# 1.0 для получения перечислителя приходилось выпол­нять немалую работу. В C# 2.0 добавлен оператор yield для легкого создания перечислите­лей. Оператор yield return возвращает один элемент коллекции и перемещает текущую позицию на следующий элемент, а оператор yield break прекращает итерацию.

В следующем примере показана реализация простой коллекции с применением опера­тора yield return. Класс HelloCollectionимеет метод GetEnumerator(). Реализация метода GetEnumerator () содержит два оператора yield return, где возвращаются стро­ки "Hello" и "World".

using System;

using System.Collections;

 

namespace Wrox.ProCSharp.Arrays

{

public class HelloCollection

{

public XEnumerator<string> GetEnumerator()

{

yield return "Hello";

yield return "World";

}

}

Метод или свойство, содержащее операторы yield, также известно как блок итератора. Блок итератора должен быть объявлен для возврата интерфейса IEnumerator или IEnumerable либо их обобщенных версий. Этот блок мо­жет содержать множество операторов yield return или yield break; оператор return не разрешен.

Теперь возможно провести итерацию по коллекции с использованием оператора foreach:

public void HelloWorld()

{

var helloCollection = new HelloCollection();

foreach (string s in helloCollection)

{

Console.WriteLine(s);

}

}

}

С блоком итератора компилятор генерирует тип yield, включая конечный автомат, как показано в следующем фрагменте кода. Тип yield реализует свойства и методы интер­фейсов IEnumerator и IDisposable. В примере можно видеть тип yield как внутренний класс Enumerator. Метод GetEnumerator() внешнего класса создает экземпляр и возвра­щает тип yield. Внутри типа yield переменная state определяет текущую позицию ите­рации и изменяется каждый раз, когда вызывается метод MoveNext(). Метод MoveNext() инкапсулирует код блока итератора и устанавливает значение текущей переменной таким образом, что свойство Current возвращает объект, зависящий от позиции.

 

public class HelloCollection

{

public IEnumerator GetEnumerator()

{

return new Enumerator (0);

}

public class Enumerator: IEnumerator<string>,IEnumerator,IDisposable

{

private int state;

private object current;

public Enumerator(int state)

{

this.state = state;

}

bool System.Collections.IEnumerator.MoveNext()

{

switch (state)

{

case 0:

current = "Hello";

state = 1;

return true;

case 1:

current = "World";

state = 2;

return true;

case 2:

break;

}

return false;

}

string System.Collections.Generic.IEnumerator<string>.Current

{

get

{

return current;

}

}

object System.Collections.IEnumerator.Current

{

get

{

return current;

}

}

void IDisposable.Dispose()

{

}

}

}

Помните, что оператор уield создает перечислитель, а не просто список, запол­няемый элементами. Этот перечислитель вызывается оператором foreach. По мере того, как элементы друг за другом извлекаются из foreach, происхо­дит обращение к перечислителю. Это обеспечивает возможность итерации по огромным объемам данных без необходимости читать их целиком в память в один прием.

 





Поделиться с друзьями:


Дата добавления: 2017-02-25; Мы поможем в написании ваших работ!; просмотров: 440 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Либо вы управляете вашим днем, либо день управляет вами. © Джим Рон
==> читать все изречения...

2236 - | 1973 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.01 с.