Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Questions

.pdf
Скачиваний:
9
Добавлен:
11.03.2016
Размер:
1.14 Mб
Скачать

Обсудите то, что вам удалось узнать об увеличении размера массива.

После создания массива как объекта невозможно изменять его размер. Возможно лишь создать новый объект-массив и ссылку на него передать в интересующую нас переменную.

Старый же объект-массив все равно будет существовать еще некоторое время, пока сборщик мусора не удалит его.

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

Классы коллекций делятся на три основных категории: общего назначения,

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

Объект класса ArrayList представляет собой массив переменной длины, элементами которого являются объектные ссылки. Любой объект класса ArrayList создается с некоторым начальным размером. При превышении этого размера коллекция автоматически его увеличивает. В случае удаления объектов массив можно сократить.

6. Опишите ситуации, в которых предпочтительней использовать массивы, а не коллекции.

Массивы лучше использовать в тех случаях, когда заранее известно количество элементов массива и при этом все элементы имеют один тип. Если типы элементов разные, то лучше использовать коллекции, но и в случае с обычным массивом тоже можно взаимодействовать если тип массива объявить object. Кроме того, массивы позволяют реализовывать многомерность своей структуры и могут содержать в себе другие массивы.

7. Опишите ситуации, в которых предпочтительней использовать коллекции, а не массивы.

Коллекции удобны тем, что, на мой взгляд, очень гибкие в отношении проведения операций над их элементами. Главное достоинство коллекций и преимущество над массивами – это то, что массивы не имеют возможности динамически изменять когда-либо объявленный их размер, в

коллекциях же, напротив, достаточно легко реализовывается добавление (удаление) ее элементов и многое другое. Если мы заранее не знаем будущий размер уже сейчас используемого массива, то логичнее воспользоваться определенным классом коллекции. А если еще все элементы коллекции имеют один тип, тогда имеет смысл использовать обобщенные коллекции.

3. Управляющие конструкции. Классы. Структуры

Остановимся на различиях между классами (типами-ссылками, reference-types) и структурами

(типами-значениями, value-types).

Классы — это ссылочные типы. Это означает, что к объектам классов доступ осуществляется через ссылку. Этим они отличаются от типов значений, к которым в С#

реализован прямой доступ. Доступ к объектам классов через ссылки увеличивает расходы системных ресурсов, в том числе и памяти. Структура подобна классу, но она относится к типу значений, а не к ссылочным типам. Структуры не могут наследовать другие структуры или классы. Структуры не могут использоваться в качестве базовых для других структур или классов. (Однако, подобно другим С#-типам, структуры наследуют класс object). Структура может реализовать один или несколько интерфейсов. Структуры могут также определять конструкторы, но не деструкторы. Однако для структуры нельзя определить конструктор по умолчанию (без параметров). Дело в том, что конструктор по умолчанию автоматически определяется для всех структур, и его изменить нельзя. Поскольку структуры не поддерживают наследования, члены структуры нельзя определять с использованием модификаторов abstract, virtual или protected. Объект структуры можно создать с помощью оператора new, подобно любому объекту класса, но это не обязательно. Если использовать оператор new, вызывается указанный конструктор, а если не использовать его, объект все равно будет создан, но не инициализирован. В этом случае необходимо выполнить инициализацию вручную. Приприсваивании одной структуры другой создается копия этого объекта. Это — очень важное отличие struct-

объекта от сlass-объекта.

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

что при использовании структур расходуется меньший объем памяти. Более того, благодаря

прямому доступу к структурам, при работе с ними не снижается производительность, что имеет место при доступе к объектам классов. Поскольку классы — ссылочные типы, доступ к их объектам осуществляется через ссылки. Такая косвенность увеличивает затраты системных ресурсов при каждом доступе. Структуры этим не страдают. В общем случае, если вам нужно хранить небольшую группу связанных данных, но не нужно обеспечивать наследование и использовать другие достоинства ссылочных типов, тип struct может оказаться более предпочтительным вариантом.

Однако, при передаче структур в методы в качестве параметров, передаются непосредственно сами структуры, если происходит передача объекта некоторого класса, то передается ссылка, а не сам объект. Передача ссылки более быстрый и менее ресурсоёмкий процесс.

1. Пусть есть следующее объявление строки:

string s = "One, Two, Three, Four, Five";

- напишите программу, которая разделить строку на элементы, отделенные запятыми

(воспользуйтесь функцией String.Split) и помещает их в массив.

- переберите элементы массива используя сначала оператор foreach, а потом еще раз, используя оператор for.

Пример:

using System;

public class Split

{

public static void Main()

{

string s = "One, Two, Three, Four, Five"; //Выводим начальное значение

Console.WriteLine("Начальная строка s имеет вид:\n"); Console.WriteLine("s = \"" + s + "\"\n"); Console.WriteLine("=================================\n");

//Заменяем в строке пары символов ", " на " " s = s.Replace(", ", " ");

//Выводим результат замен

Console.WriteLine("\n\nТеперь, обработанная строка s имеет вид:\n"); Console.WriteLine("s = \"" + s + "\"\n"); Console.WriteLine("=================================\n\n\n\nМассив split

содержит элементы:\n");

//Объявляем массив split и заносим в него элементы со строки s string[] split = s.Split(' ');

//string [] split = words.Split(new Char [] {' ', ',', '.', ':'});

//Выводим элементы массива на консоль

Console.WriteLine("\n\nВыводит конструкция foreach\n"); int m = 0;

foreach (string n in split)

{

Console.WriteLine("split [" + m + "] = " + n); m++;

}

Console.WriteLine("\n\nВыводит конструкция for\n");

for (int i = 0; i < split.Length; i++)//foreach (string x in split)

{

Console.WriteLine("split [" + i + "] = " + split[i]);

}

Console.ReadLine();

}

}

Мы разбили строку относительно пробелов между словами. Можно было и убрать все пробелы и разделить строку поэлементно по заглавным буквам.

2. Изучите нижеприведенный пример и объясните, почему выбрасывается исключение когда вызывается метод CheckType с параметром string, но работает нормально, когда в качестве параметра этому методу передается значение типа int.

using System;

class IsExample

{

static void Main(string[] args)

{

int i = 1;

string s = "Hello, world"; CheckType(i); CheckType(s);

}

static void CheckType(object o)

{

int j; string t;

if (o is int)

{

Console.WriteLine("o is an int");

}

else

{

Console.WriteLine("o is not an int");

}

j = (int) o;

}

}

Дело в том, что запись j = (int) o; нормально работает когда переменная о содержит значение типа int (или совместимого с ним – конвертируемого), а при хранении ею стрингового значения операция явного приведения типов дает исключение поскольку тип string и int не являются взаимно конвертируемыми типами.

Приемр:

using System;

class IsExample

{

static void Main(string[] args)

{

int i = 1;

string s = "Hello, world"; CheckType(i); CheckType(s); Console.Read();

}

static void CheckType(object o)

{

int j; string t;

if (o is int)

{

Console.WriteLine("o is an int");

}

else

{

Console.WriteLine("o is not an int");

}

try

{

j = (int)o;

}

catch

{

Console.WriteLine("Ошибка конвертации: невозможно преобразовать в тип

интежер");

}

}

}

3. Скомпилируйте следующий код:

using System;

class SimpleTypes

{

static public void Main(string[] args)

{

CalculateSum();

}

static void CalculateSum()

{

byte a, b; a = 255; b = 122;

Console.Out.WriteLine((byte) (a * b)); Console.Out.WriteLine(a * b);

}

}

Ответьте на вопросы:

- почему в консоль выводятся два разных значения?

Дело в том, что при умножении байтовых значений, результат возвращается типа int, но не byte. Это потому, что байт может содержать максимум восемь бит данных, а умножение двух байт скорее всего даст результат, который не уместится в один тип byte. Поэтому используется неявное преобразование типов от меньшего к большему для исключения потери данных. Но если мы хотим получить результат в байтах, то необходимо осуществить процедуру явного преобразования результата типа интежер в тип байт.

4. Какие варианты из следующих:

 

A. do {i++;} while {i <= 10};

--

B. do {i++;} while (i <= 10);

+

C. do() {i++;} while() {i <= 10};

--

D. do() {i++;} while (i <= 10);

--

E. do (i++;) while (i <= 10);

--

являются правильными циклами do/while? Почему остальные варианты не верны? Обсудите на форуме.

A, C, D, E – неправильные. B – правильное.

В варианте A нарушен синтаксис – после ключевого слова while операторы должны быть записаны в круглых, а не фигурных скобках. В вариантах C и D наличие подряд идущих пар круглых и фигурных скобок недопустимо. После ключевого слова do необязательны фигурные скобки (если один оператор), то-есть возможна пара круглых скобок, но обязательно круглые скобки должны быть размещены внутри фигурных.

5. Структура и класс – что из них является типом-значением (value-type), а что типом-ссылкой

(reference-type)? Где создаются объекты каждого типа? Структура – это тип-значения. Класс – тип-ссылка. Соответственно размещаются в стеке и куче.

Проанализируйте, скомпилируйте и запустите файл stuct.cs и объясните как он работает, учитывая разницу между типами-значениями (value-types) и типами-ссылками (reference-types)

using System;

struct PointStruct

{

public int x; public int y;

}

class PointClass

{

public int x; public int y;

}

class StructureExample

{

static void Main()

{

PointStruct ps1; ps1.x = 10; ps1.y = 20;

PointStruct ps2 = ps1; ps2.x = -10;

ps2.y = -20;

Console.WriteLine("Examining the behavior of structures");

Console.WriteLine("ps1.x = " + ps1.x);

Console.WriteLine("ps1.y = " + ps1.y);

Console.WriteLine("ps2.x = " + ps2.x);

Console.WriteLine("ps2.y = " + ps2.y);

PointClass pc1 = new PointClass(); pc1.x = 10;

pc1.y = 20;

PointClass pc2 = pc1; pc2.x = -10;

pc2.y = -20;

Console.WriteLine("Examining the behavior of classes"); Console.WriteLine("pc1.x = " + pc1.x); Console.WriteLine("pc1.y = " + pc1.y); Console.WriteLine("pc2.x = " + pc2.x); Console.WriteLine("pc2.y = " + pc2.y);

}

}

Работает следующим образом.

Описывается структура PointStruct, описыается класс PointClass. Создается структурный объект ps1 и его внутренним переменным присваиваются значения. Потом создается новый структурный объект ps2 и в него копируется по значению все содержимое ps1. Внутренним переменным ps2 присваиваются новые значения. Изменения значений переменных ps2 не отражаются на значениях переменных ps1 поскольку они – два разных объекта (передача данных произошла по значению, - путем создания новой копии данных).

Аналогичные действия проведены для классовых объектов. Только здесь происходит передача значений по ссылке и создание нового объекта не происходит, только новая ссылка на старый объект организовывается. Потому изменения в объекте через ссылку pc2 отображается и на ссылке pc1, если так можно выразится.

В чем ключевые отличия между классами и структурами, по отношению к типам-ссылкам

(reference-types) и типам-значениям (value-types)?

Классы – ссылочные типы. Их объекты создаются в управляемой куче. Структуры – значимые типы. Их объекты создаются в стэке. Отсюда и разный принцип работы с ними.

-где размещаются переменные ps1 и ps2? В стеке.

-где размещаются переменные pc1 и pc2? В куче.

-происходит ли выделение памяти для значения переменной pc2 на самом деле? Почему? Нет.

Потому, что опущен оператор pc2 = new PointClass(); А ключевое слово new как раз и выделяет область памяти для прежде объявленной некой переменной. В нашем случае записью PointClass pc1 = new PointClass(); мы объявили и выдели область пямяти под ссылочную переменную типа

PointClass pc1, а записью PointClass pc2 = pc1; мы объявили ссылочную переменную pc2 (тоже типа PointClass), но памяти под объект не выделили поскольку нового объекта то для нее (для pc2)

не создали, а лишь «сослали» на уже существующий объект – на уже существующую область памяти.

- Почему присвоение значений полям ps2.x и ps2.y НЕ оказывает влияния на поля ps1.x и ps1.y?

Потому что присвоение этим переменным происходит не по ссылке а по значению, то-есть создается новая копия данных, стоящих справа от оператора присваивания.

- Почему присвоение значений полям pc2.x и pc2.y оказывает влияние на значение полей pc1.x и pc2.y? Потому, что здесь присваивается ссылка (адресс) на объект, который все-равно остается в единственном числе. И pc2.x, и pc1.x (аналогичнго и для у) ссылаются на один объект – являются разными ссылками на Единственный объект.

6. В C# всё является объектами. Приведите короткий пример, демонстрирующий это утверждение. int i = new int ( ) ; - ключевое слово new в первую очередь предназначено для создания объектов и выделения под них памяти. Выше приведенная запись возможна. И для типа-значения (int) мы воспользовались оператором new (предназначенным для использования в ссылочных типах). Но в этом случае динамического выделения памяти не происходит. А переменная i инициализируется нулем.

Кроме того, все типы данных унаследованы от класса object.

4. Перегрузка методов и операторов

Рассмотрим следующий пример, содержащий три метода взгляните на код приведенный Speak.

using System;

class Dog

{

public string s;

}

class Cat

{

public string s;

}

//An example to demonstrate method overloading (and not the best example regarding

//object oriented programming!)

class OverloadExample

{

static void Main()

{

Dog dog = new Dog(); dog.s = "I'm a dog";

Cat cat = new Cat(); cat.s = "I'm a cat";

Speak(dog);

Speak(cat);

Speak((object) dog); // Explicit cast to force the right overloaded method to be called

}

// Speak is overloaded three times: one method expects a Dog as it's argument,

the

// second expects a Cat as it's argument and the third expects an object as it's argument.

static void Speak(Dog d)

{

Console.WriteLine("A dog barks");

}

static void Speak(Cat c)

{

Console.WriteLine("A cat meows");

}

static void Speak(object o)

{

Console.WriteLine("I have no idea what an object does.");

}

}

Когда вы скомпилируете и запустите этот пример, то увидите, что когда мы вызываем метод Speak

передавая объект типа Dog, то этот метод печатает “A dog barks”. Аналогично, если мы передаем в метод Speak объект типа Cat, этот метод печатает “A cat meows”. Третий интересный пример – метод Speak, которому передается переменная типа object. Учитывая, что в C# все является объектами, Вы можете спросить – каким образом компилятор различает – какой метод вызывать для объектов типа Dog и Cat. Компилятор выбирает такой метод, который наиболее точно соответствует типу параметров. Если точное соответствие не найдено, компилятор начинает исследовать дерево наследования и проверять, можно ли вызвать какой-либо метод для базовых классов. Если и в этом случае не найдено соответствие – компилятор выдает сообщение об ошибке.

В нашем примере мы специально преобразовали объект типа Dog к базовому типу, чтобы был вызван метод с сигнатурой Speak(object o).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]