Книжная полка Сохранить
Размер шрифта:
А
А
А
|  Шрифт:
Arial
Times
|  Интервал:
Стандартный
Средний
Большой
|  Цвет сайта:
Ц
Ц
Ц
Ц
Ц

Особенности языка C# 5.0 .NET FRAMEWORK 4.5

Покупка
Основная коллекция
Артикул: 384200.01.99
Доступ онлайн
от 120 ₽
В корзину
Особенности языка C# 5.0 .NET FRAMEWORK 4.5 : методческое пособие / Ю. А. Костиков, А. В. Мокряков, В. Ю. Павлов [и др.]. - Москва : НИЦ ИНФРА-М, 2015. - 38 с. - ISBN 978-5-16-103256-5. - Текст : электронный. - URL: https://znanium.com/catalog/product/515184 (дата обращения: 27.04.2024). – Режим доступа: по подписке.
Фрагмент текстового слоя документа размещен для индексирующих роботов. Для полноценной работы с документом, пожалуйста, перейдите в ридер.
ОСОБЕННОСТИ ЯЗЫКА C# 
5.0 .NET FRAMEWORK 4.5 

Методическое пособие для студентов

Костиков Ю. А., Мокряков А. В., Павлов В. Ю., Романенков А. М.

МОСКВА

Содержание.

Кортежи.
2

Основные потоки приложения.
3

Некоторые оптимизации GC.
4

Класс System.IO.Path.
6

Исключения.
7

События.
11

Элементы Window Forms.
13

Упражнения
21

ADO.NET. Подключаемый уровень
23

ADO.NET. Автономный уровень
29

Кортежи.

Допустим, мы хотим написать функцию, которая возвращает небольшой набор 

некоторых значений разного типа. Возникает вопрос - как возвращать эти данные?

Так, проблему можно решить несколькими способами:

1) сделать возвращаемым значением функции object или object[].

2) Использовать анонимные типы и dynamic.

3) Использовать ref/out параметры.

3) Объявить и использовать свою структуру данных.


Недостаток первого подхода заключается в том, что здесь будет происходить 

процесс 
упаковки/распаковки 
объектов, 
что 
снижает 
типобезопасность, 

читабельность и  производительность программы.


Динамическая типизация повышает неявность кода. И вообще её следует избегать 

при любой возможности.


ref/out параметры накладывают ограничения на код, так же теряется логическая 

связь значений.


Использование собственной структуры данных кажется оптимальным вариантом. 

Но давайте предположим, что наша функция - лишь малая часть какого либо 

проекта.  И у нас не одна, а множество подобных вспомогательных функций. 

Объявление "второстепенных" типов данных снижает читабельность кода и 

занимает немало времени. 

Для решение данной проблемы в язык C# ввели кортежи(Tuple) - шаблонные 

классы, которые комбинируют значения разных типов. Существует 8(по количеству 

типов-параметров) шаблонных классов Tuple, а так же статический класс Tuple, 

содержащий метод Create, который облегчает создание кортежей.

Пример:

static Tuple<bool, int, string> Foo()

{

//...

// две эквивалентных строчки.

//return new Tuple<bool, int, string>(true, 20, "Alex"); 

return Tuple.Create(true, 20, "Alex");

}

{

//...

var val = Foo();

if (val.Item1)

Console.WriteLine("{0} - {1}", val.Item2, val.Item3);

}

ItemX - свойство только для чтения. Важно, что все элементы кортежа 

являются статически типизированными.

Кортеж является ссылочным типом. Однако, метод  Equals экземпляра типа 

перегружен так, что сравнивает значения полей. Так же кортеж удобно использовать 

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

Основные потоки приложения.

Если вы запустите простое консольное приложение, то с помощью монитора 

ресурсов Windows можно обнаружить, что в программе запущен вовсе не один 

поток, а несколько больше. В чём же дело ?

При запуске любого .NET приложения запускаются следующие потоки:

1) Главный поток

2) Поток(-и) сборщика мусора (GC)

3) Поток финализации*

4) Поток, отслеживающий системные события.

В действительности, потоков может быть больше, особенно в Debug-сборке.

Под системными событиями подразумеваются изменения настроек и 

параметров дисплея, изменение системной коллекции шрифтов, завершение работы 

системы, изменение системных параметров, остановка и возобновление работы 

системы. Данные события определены в классе Microsoft.Win32.SystemEvent.

* Если у объекта реализован метод Finalize(), то он при сборке мусора 

помещается в отдельную очередь объектов, ожидающих вызова Finalize, которая 

обрабатывается в отдельном высокоприоритетном потоке.

Некоторые оптимизации GC.

1. Использование нескольких процессоров

Эта оптимизация предполагает, что на многопроцессорной машине собирать 

мусор можно, используя сразу несколько процессоров. Чем больше процессоров, 

тем больше скорость сборки мусора. Однако если куча одна, то доступ к ее структуре 

придется синхронизировать. А это может свести на нет все преимущества 

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

каждый процессор. Для этого нужно подправить конфигурационный файл 

приложения. 

<runtime>

<gcServer enabled="true"/>

</runtime>

По умолчанию данное свойство выставлено в false. Это можно 

проверить, вызвав свойство System.Runtime.GCSettings.IsServerGC

Данная оптимизация увеличивает количество потоков приложения примерно на 

число процессоров(ядер).

2. Параллельная сборка мусора.

По умолчанию, поток(-и) GC в фоновом режиме ведёт(-ут) процесс 

построения графа объектов без приостановки управляемых потоков. Поскольку в 

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

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

многих случаях такое замедление незаметно. Зато это позволяет избавиться от 

паузы, вызванной построением графа объектов. А малый размер пауз очень важен в 

интерактивных приложениях.

Несмотря на всё данную оптимизацию можно отключить

<runtime>

<gcConcurrent enabled="false"/>

</runtime>

3. Изменение режима задержки для сборки мусора (LatencyMode).

Задержка - время, в течение которого сборщик мусора вмешивается в работу 

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

выполняющиеся потоки приложения. В некоторых ситуациях, например, при 

получении приложением данных или при отображении содержимого, полная сборка 

мусора может произойти в критическое время и привести к снижению 

производительности.

Можно настроить уровень вмешательства сборщика мусора путем задания для 

свойства System.Runtime.GCSettings.LatencyMode одного из значений System.Runtime.GCLatencyMode: 

Bath

Отключает параллельную сборку мусора. Рекомендован для приложений без 

графического интерфейса. Отключает параллельную сборку мусора переопределяет 

параметр конфигурации среды выполнения <gcConcurrent> .

Interactive

Включает параллельную сборку мусора. Самый сбалансированный режим

LowLatency

Сборщик мусора работает только с поколениями 0 и 1. Это режим 

наименьшего вмешательства. Данный режим нужно включать только при 

критических операциях. 

SustainedLowLatency

Сборщик мусора анализирует поколения 0 и 1, и фоновый(параллельный) сбор 

поколения 2. Не может использоваться, если отключена параллельная сборка 

мусора. Этот режим приводит к увеличению размера управляемой кучи больше, чем 

другие режимы. Так же он не сжимает управляемую кучу, что увеличивает 

фрагментацию. 

В Low-режимах полная сборка мусора происходит только в том случае, если 

система испытывает недостаток памяти. 

После завершения критических операций с Low-режимом нужно вернуться к 

режиму с более высокой задержкой. Это позволит GC освободить память (в 

основном за счёт поколения 2) и оптимизировать структуру управляемой кучи. 

Класс  System.IO.Path.

Данный класс позволяет кроссплатформенно работать с путями в файловой 

системе - помогает формировать корректные и определять ошибочные пути.

Упрощает основные операции с путями.

Элементы класса не работают с файловой системой, они лишь позволяют 

определить корректен ли формат пути. Для примера рассмотрим несколько строк.

Избранные свойства и функции класса:

char DirectorySeparatorChar

возвращает символ, разделяющий уровни дирректорий.

char PathSeparator

возвращает символ разделяющий пути в переменной окружения PATH.

string Path.Combine (params string[] paths)

объединяет строки-параметры в путь.

char[] Path.GetInvalidFileNameChars

возвращает символы, которые не разрешены в именах файлов.

char[] Path.GetInvalidPathChars

возвращает символы, запрёщенные в именах путей.

string Path.GetPathRoot

возвращает корневой каталог для заданного пути.

Примеры использования:

string s = Path.Combine(@"C:\", "MyDocuments", "Images");

// получим "C:\MyDocuments\Images"

Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, 

"hello"));

// ...

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

bool isError = s.Any(ch => Path.GetInvalidFileNameChars().Contains(ch));

Path.GetExtension(@"U:\doc1.txt");

// получим ".txt"

s = @"U:\doc1";

if (!Path.HasExtension(s)) s = Path.ChangeExtension(s, "txt");

// получим @"U:\doc1.txt";

// причём вызов Path.ChangeExtension(s, ".txt") вернёт аналогичную строку.

Исключения.

Часть I.

Рассмотрим пример - есть некоторая функция с объявленными блоками try
catch-finally (блок catch необязателен) :

static int Test() {

try

{

return SomeNumber();

}

finally

{

Foo();

}

}

Как известно, блок finally исполняется всегда. Поэтому, если компилятор 

встречает оператор return, то возвращаемое значение кэшируется, затем 

выполняется код блока finally, и только после этого идёт возврат из функции.

Выше написанный код при компиляции развернётся примерно так :

static int Test()

{

int CS$1$0000;

try

{

CS$1$0000 = SomeNumber();

}

finally

{

Foo();

}

return CS$1$0000;

}

Примерно так выглядит IL-код.

.method private hidebysig static int32 Test() cil managed

{

.maxstack 1

.locals init (

[0] int32 CS$1$0000)

L_0000: call int32 Program::SomeNumber()

L_0005: stloc.0

L_0006: leave.s L_000e

L_0008: call void Program::Foo()

L_000d: endfinally 

L_000e: ldloc.0

L_000f: ret 

.try L_0000 to L_0008 finally handler L_0008 to L_000e

}

Поэтому будьте внимательны в написании кода, если возвращаемым 

значением является некоторый ресурс, который освобождается в блоке finally. В 

таком случае, вы, скорее всего, ошиблись с архитектурой.

Следующая конструкция не верна, т.к. оператор throw без параметров может 

быть только внутри блока catch.

try

{

//...

}

finally

{

throw; // !

}

И следующий код тоже ошибочен (правда это другой номер ошибки). Хоть теперь 

вызов throw, в отличие от пред. примера находится внутри блока catch. Дело в том, 

что внутренний finally ничего не знает о внешнем исключении (и очевидно, 

внутренний finally относится только к внутр. try)

try

{

}

catch

{

try

{

}

finally

{

throw;

}

}

Также стоит отметить, что в блоке finally запрещён возврат значения из 

функции.

Часть II.

На самом деле платформа .NET
после возбуждения исключения в 

управляемом коде не сразу ищёт catch-блоки. Сразу после возникновения 

исключения генерируется событие AppDomain.CurrentDomain.FirstChanceException,

аргумент которого содержит в себе само исключение. Если у события есть 

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

потом среда ищет подходящие catch-блоки. И в случае, если исключение не было 

обработано, 
в 
любом 
типе 
.NET
приложения 
генерируется 
событие 

AppDomain.CurrentDomain.UnhandledException, которое содержит в себе объект 

исключения и доступное только для чтения свойство  IsTerminating, показывающее, 

будет ли завершён процесс. Данное событие является как бы обработчиком 

необработанных исключений CLR.

Только потом происходит выполнение блока кода finally (если он есть).

Для обоих вышеуказанных событий объектом события является тот домен, в 

котором был создан поток, в котором возникло исключение. По умолчанию, если вы 

не создаёте отдельные домены, то это будет домен приложения - AppDomain.

Вернёмся к моменту, если исключение не было обработано. По умолчанию, 

процесс будет завершён. Однако существует способ "смягчить" реакцию CLR на 

необработанные исключения.

Для этого в конфигурационном файле приложения нужно в секцию <runtime> в 

секции <configuration> добавить следующую строку:

<legacyUnhandledExceptionPolicy enabled="1"/>

Теперь в результате необработанного исключения будет завершаться только 

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

в консольных приложениях всё равно будет выведена информация об исключении 

(даже если это фоновый поток). Что бы этого избежать нужно подписаться на 

событие UnhandledException, [тогда исключение будет считаться обработанным.]

Если в конструкторе возникнет необработанное исключение, то произойдёт 

просто размотка стека. Объект будет корректно удалён (в отличие от С++ здесь нет 

сложностей).

Доступ онлайн
от 120 ₽
В корзину