Объектно-ориентированное программирование.-6
.pdfно выполнение программы – зеленой стрелкой. Строка кода, для которой установлена точка останова, отображается на красном фоне; текущая конструкция при отладке – на желтом фоне; конструкция, на которой было остановлено выполнение программы – на зеленом фоне (рис. 2.35).
Все эти действия доступны только из среды разработки. Дополнительные команды смотрите в меню «Отладка». Прервать выполнение программы из консоли позволяет нажатие комбинации клавиш Ctrl+C или закрытие окна консоли.
Для наблюдения за значениями локальных переменных, а также полей и свойств типов достаточно навести курсор мыши на экземпляр типа. Если тип содержит вложенные экземпляры, то их также можно разворачивать для просмотра (рис. 2.35).
Также при отладке отображаются окна с дополнительной информацией, в том числе:
•«Видимые» – обзор значений видимых переменных, полей и свойств;
•«Локальные» – обзор значений локальных переменных;
•«Точки останова» – информация об имеющихся точках останова. Здесь можно некоторые точки удалить или отключить временно, а также задать условия останова;
•«Стек вызовов» – содержимое стека вызовов.
Например, на рис. 2.36 среда разработки настроена так, что отображаются окна «Видимые» и «Стек вызовов».
Рис. 2.36 – Обзор значений переменных и стека вызовов
71
При возникновении в программе необработанной исключительной ситуации, среда разработки сигнализирует об этом, показывая окно с информацией об исключении и месте его появления в программе (рис. 2.37).
Рис. 2.37 – Необработанная исключительная ситуация
Если же программа была запущена без отладки (Ctrl+F5) или не из среды разработки, то отобразится окно с информацией об ошибке (рис. 2.38). При нажатии на кнопку «ОК» информация об исключении будет выведена на консоль, и затем приложение будет закрыто. При нажатии на кнопку «Отмена» будет запущен отладчик Visual Studio.
Рис. 2.38 – Ошибка приложения
После исправления всех ошибок времени исполнения в приложении можно построить его финальную версию (Release), выбрав соответствующий пункт в выпадающем списке на панели инструментов или в свойствах проекта («Проект» → «Свойства…», закладка «Построение»).
72
3. ОСНОВЫ ЯЗЫКА C#
У нас уже есть простая, но работающая программа. Можно приступать
кизучению конструкций языка C#, добавляя их к этой программе. Рассмотрим также алфавитный список ключевых слов языка C# с крат-
ким указанием их функций и разделов учебного пособия, в которых о них говорится более подробно (табл. 3.1).
Табл. 3.1 – Список ключевых слов языка C#
КС |
Описание |
Пункт |
|
|
|
abstract |
Абстрактный класс или член класса |
4.2.1-2 |
|
|
|
as |
Преобразование типов данных |
3.3.3.7 |
|
|
|
base |
Доступ к конструктору базового класса |
4.4.2 |
|
Доступ к члену базового класса |
4.7.2.2 |
|
|
|
bool |
Логический тип данных |
3.1.2.3 |
|
|
|
break |
Завершение цикла |
3.4.4.1 |
|
Завершение оператора выбора |
3.4.2.2 |
|
|
|
byte |
Целый тип данных |
3.1.2.1 |
|
|
|
case |
Ветвление оператора выбора |
3.4.2.2 |
|
|
|
catch |
Обработка исключительной ситуации |
3.4.5.2 |
|
|
|
char |
Символьный тип данных |
3.1.2.4 |
|
|
|
checked |
Включение проверки переполнения для целых типов |
3.3.3.10 |
|
|
|
class |
Описание класса |
4.2 |
|
Ограничение параметров типа |
5.1.2 |
|
|
|
const |
Описание неизменной локальной переменной |
3.1.6.3 |
|
Описание неизменного члена класса |
4.3.1 |
|
|
|
continue |
Переход на следующую итерацию цикла |
3.4.4.2 |
|
|
|
decimal |
Тип данных с плавающей точкой |
3.1.2.2 |
|
|
|
default |
Метка по умолчанию в операторе выбора |
3.4.2.2 |
|
Значение типа по умолчанию |
3.1 |
|
|
|
delegate |
Описание делегата |
4.8.2 |
|
|
|
do |
Оператор цикла |
3.4.3.2 |
|
|
|
double |
Тип данных с плавающей точкой |
3.1.2.2 |
|
|
|
else |
Часть условного оператора |
3.4.2.1 |
|
|
|
enum |
Объявление перечисления |
3.1.2.5 |
|
|
|
|
73 |
|
event |
Объявление события |
4.8.3 |
|
Описание атрибута события |
5.4.1 |
|
|
|
explicit |
Объявление оператора явного преобразования типов |
4.7.3.4 |
|
|
|
extern |
Объявление метода с внешней реализацией |
4.2.2 |
|
Описание внешнего псевдонима |
4.1.3.2 |
|
|
|
false |
Логическая константа «ложь» |
3.1.2.3 |
|
Логический оператор |
3.3.3.3 |
|
|
|
finally |
Завершение блока обработки исключительной ситуации |
3.4.5.2 |
|
|
|
fixed |
Объявление неперемещаемой переменной или буфера |
5.5.2.3 |
|
|
|
float |
Тип данных с плавающей точкой |
3.1.2.2 |
|
|
|
for |
Оператор цикла |
3.4.3.3 |
|
|
|
foreach |
Оператор цикла |
3.4.3.4 |
|
|
|
goto |
Переход на ветку оператора выбора |
3.4.2.2 |
|
Переход на именованную метку |
3.4.4.3 |
|
|
|
if |
Условный оператор |
3.4.2.1 |
|
|
|
implicit |
Объявление оператора неявного преобразования типов |
4.7.3.4 |
|
|
|
in |
Часть оператора цикла foreach |
3.4.3.4 |
|
Часть выражения запроса LINQ |
– |
|
|
|
int |
Целый тип данных |
3.1.2.1 |
|
|
|
interface |
Объявление интерфейса |
4.9.1 |
|
|
|
internal |
Модификатор доступа |
4.2.1-2 |
|
|
|
is |
Проверяет совместимость объекта с заданным типом |
3.3.3.9 |
|
|
|
lock |
Блокировка фрагмента кода |
5.2.3.2 |
|
|
|
long |
Целый тип данных |
3.1.2.1 |
|
|
|
namespace |
Объявление пространства имен |
4.1.1 |
|
|
|
new |
Оператор создания экземпляра объекта |
3.3.3.1 |
|
Сокрытие члена базового класса |
4.2.1-2 |
|
Ограничение типов в универсальном объявлении |
5.1.2 |
|
Часть выражения запроса LINQ |
– |
|
|
|
null |
Пустая ссылка |
3.1.3 |
|
|
|
object |
Псевдоним класса Object |
3.1.3 |
|
|
|
operator |
Перегрузка встроенного оператора |
4.7.3.1 |
|
|
|
out |
Передача аргумента в метод по ссылке |
4.4.1.1 |
|
|
|
override |
Перегрузка абстрактного/виртуального члена класса |
4.6.2.2 |
|
|
|
|
74 |
|
params |
Произвольное количество аргументов метода |
4.4.1.3 |
|
|
|
private |
Модификатор доступа |
4.2.1-2 |
|
|
|
protected |
Модификатор доступа |
4.2.1-2 |
|
|
|
public |
Модификатор доступа |
4.2.1-2 |
|
|
|
readonly |
Модификатор доступа |
4.3.2 |
|
|
|
ref |
Передача аргумента в метод по ссылке |
4.4.1.1 |
|
|
|
return |
Прерывание выполнения метода |
3.4.4.4 |
|
Описание атрибута возвращаемого значения |
5.4.1 |
|
|
|
sbyte |
Целый тип данных |
3.1.2.1 |
|
|
|
sealed |
Описание изолированного класса или члена класса |
4.2.1-2 |
|
|
|
short |
Целый тип данных |
3.1.2.1 |
|
|
|
sizeof |
Размер типа данных по значению |
3.3.3.9 |
|
|
|
stackalloc |
Выделение блока памяти в стеке |
5.5.2.4 |
|
|
|
static |
Объявление статического класса или члена класса |
4.2.1-2 |
|
|
|
string |
Псевдоним класса String |
3.1.3.1 |
|
|
|
struct |
Объявление структуры |
3.1.2.6 |
|
Ограничение параметров типа |
5.1.2 |
|
|
|
switch |
Оператор выбора |
3.4.2.2 |
|
|
|
this |
Ссылка на текущий экземпляр класса |
4.2.3 |
|
Объявление индексатора |
4.5.2 |
|
Описание метода расширения |
4.4.1.2 |
|
|
|
throw |
Генерация исключительной ситуации |
3.4.5.2 |
|
|
|
true |
Логическая константа «истина» |
3.1.2.3 |
|
Логический оператор |
3.3.3.3 |
|
|
|
try |
Блок обработки исключительных ситуаций |
3.4.5.2 |
|
|
|
typeof |
Получение метакласса типа |
3.3.3.9 |
|
|
|
uint |
Целый тип данных |
3.1.2.1 |
|
|
|
ulong |
Целый тип данных |
3.1.2.1 |
|
|
|
unchecked |
Выключение проверки переполнения |
3.3.3.10 |
|
|
|
unsafe |
Блок небезопасного (неуправляемого) кода |
5.5.2.1 |
|
|
|
ushort |
Целый тип данных |
3.1.2.1 |
|
|
|
using |
Использование пространства имен или псевдонима |
4.1.2 |
|
Оператор управления ресурсами |
3.5.3 |
|
|
|
virtual |
Объявление виртуального члена класса |
4.7.2.1 |
|
|
|
|
75 |
|
void |
Объявление метода, не возвращающего значение |
4.4.1 |
|
|
|
volatile |
Объявление поля, изменяемого несколькими потоками |
5.2.3.5 |
|
|
|
while |
Оператор цикла |
3.4.3.1 |
|
|
|
Также есть ряд контекстно-зависимых ключевых слов (табл. 3.2). Т.е. они считаются таковыми только в определенных конструкциях языка.
Табл. 3.2 – Контекстно-зависимые ключевые слова языка C#
КС |
Описание |
Пункт |
|
|
|
add |
Метод добавления обработчика события |
4.8.3 |
|
|
|
alias |
Описание внешнего псевдонима |
4.1.3.2 |
|
|
|
ascending |
Часть выражения запроса LINQ |
– |
|
|
|
assembly |
Описание атрибута сборки |
5.4.1 |
|
|
|
descending |
Часть выражения запроса LINQ |
– |
|
|
|
by |
Часть выражения запроса LINQ |
– |
|
|
|
equals |
Часть выражения запроса LINQ |
– |
|
|
|
field |
Описание атрибута поля |
5.4.1 |
|
|
|
from |
Часть выражения запроса LINQ |
– |
|
|
|
get |
Описание метода чтения значения свойства |
4.5.1.1 |
|
|
|
global |
Псевдоним глобального пространства имен |
4.1.3.2 |
|
|
|
group |
Часть выражения запроса LINQ |
– |
|
|
|
into |
Часть выражения запроса LINQ |
– |
|
|
|
join |
Часть выражения запроса LINQ |
– |
|
|
|
let |
Часть выражения запроса LINQ |
– |
|
|
|
method |
Описание атрибута метода |
5.4.1 |
|
|
|
module |
Описание атрибута модуля |
5.4.1 |
|
|
|
on |
Часть выражения запроса LINQ |
– |
|
|
|
orderby |
Часть выражения запроса LINQ |
– |
|
|
|
param |
Описание атрибута параметра |
5.4.1 |
|
|
|
partial |
Частичное определение объекта или метода |
4.2.1-2 |
|
|
|
property |
Описание атрибута свойства |
5.4.1 |
|
|
|
remove |
Метод удаления обработчика события |
4.8.3 |
|
|
|
select |
Часть выражения запроса LINQ |
– |
|
|
|
set |
Описание метода записи значения свойства |
4.5.1.1 |
|
|
|
type |
Описание атрибута типа |
5.4.1 |
|
|
|
value |
Доступ к записываемому значению свойства |
4.5.1.1 |
|
|
|
|
76 |
|
|
Доступ к обработчику события |
4.8.3 |
|
|
|
var |
Объявление локальной переменной неявного типа |
3.1.4 |
|
|
|
where |
Ограничение типов в универсальном объявлении |
5.1.2 |
|
Часть выражения запроса LINQ |
– |
|
|
|
yield |
Управление настраиваемым итератором |
3.4.4.6 |
|
|
|
Вне указанных конструкций они ключевыми словами не считаются. Например, можно объявить переменную с именем «value», но только вне описания свойства или события.
77
§ 3.1. Типы данных. Идентификаторы
Все типы данных делятся на два больших класса – типы данных по значению и типы данных по ссылке. Существуют и указатели, но их можно использовать только в небезопасном коде, о них мы поговорим позже, в § 5.5. Отметим одно важное отличие языка C# от языка C++. В языке C++ конструкция, подобная следующей:
<тип> <имя переменной>;
объявляет новую переменную типа <тип> с указанным именем. Причем для переменной сразу же выделяется место в памяти на стеке, а если тип – это класс, то после этого вызывается конструктор данного класса. В языке C# этого не происходит! Память нужно выделять самостоятельно, используя оператор new:
<тип> <имя переменной> = new <тип>([<аргументы конструктора>]);
Существуют и другие способы инициализации переменных, о них мы поговорим ниже.
Каждый тип данных имеет значение по умолчанию (т.е. значение, принимаемое при инициализации конструктором по умолчанию, если он доступен):
•0 для целых типов;
•0.0 для чисел с плавающей точкой;
•false для булева типа;
•'\u0000' для символьного типа;
•0 для перечисляемого типа (даже если константа с таким значением не определена);
•null для ссылочных типов;
•пустое значение для обнуляемых типов.
Для получения значения по умолчанию в явном виде также используется ключевое слово default:
<тип> <имя переменной> = default(<тип>);
3.1.1. Базовый класс System.Object
Все объекты .NET имеют единый базовый класс – System.Object. Соответственно, все типы данных (а они, очевидно, также являются классами)
78
наследуются от Object. И все методы, объявленные в классе Object, доступны для любого типа данных C#. Самые полезные из них – это:
public virtual string ToString(); public virtual bool Equals(object obj);
public static bool Equals(object objA, object objB);
public static bool ReferenceEquals(object objA, object objB); public Type GetType();
Первый метод возвращает строковое представление класса. Этот метод является виртуальным, поэтому то, как он будет работать, зависит от логики классов-потомков.
Следующие два метода проверяют эквивалентность двух экземпляров классов .NET. Причем второй вариант метода, статический, реализован через первый, поэтому запись Object.Equals(A, B) эквивалентна записи A.Equals(B) (но не B.Equals(A)!). При перегрузке метода Equals также следует перегрузить метод GetHashCode (см. п. 4.7.3.5).
Пример, демонстрирующий перегрузку методов ToString и Equals:
class Five
{
public override string ToString()
{
return "Five";
}
public override bool Equals(object obj)
{
return obj.ToString() == "Seven";
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
class Seven
{
public override string ToString()
{
return "Seven";
}
public override bool Equals(object obj)
{
return obj.ToString() == "Seven";
}
public override int GetHashCode()
{
return base.GetHashCode();
}
79
}
static int Main(string[] args)
{
Five c5 = new Five(); Seven c7 = new Seven(); Console.WriteLine(c5); Console.WriteLine(c7);
Console.WriteLine(c5.Equals(c7));
Console.WriteLine(c7.Equals(c5)); Console.WriteLine(Object.Equals(c5, c7)); Console.WriteLine(Object.Equals(c7, c5)); return 0;
}
В данном примере мы пользуемся тем, что первый вариант метода Equals виртуальный, и перегружаем его (см. § 4.7). Также перегружаем методы ToString. Теперь класс Five считает, что он эквивалентен тому классу, чье строковое представление – «Seven». Класс Seven считает, что он также эквивалентен такому классу. В результате работы на консоль будет выведен следующий текст:
Five
Seven
True
False
True
False
Следовательно, в некоторых ситуациях запись A.Equals(B) не эквивалентна записи B.Equals(A).
Четвертый метод проверяет совпадение ссылок на объекты. Рассмотрим следующий пример:
int x1 = new int(); int y1 = x1;
Console.WriteLine("x1 = " + x1 + "; y1 = " + y1); Console.WriteLine(x1.Equals(y1) ? "x1 == y1" : "x1 != y1"); Console.WriteLine(Object.ReferenceEquals(x1, y1) ?
"ref x1 == ref y1" : "ref x1 != ref y1");
Object x2 = new Object(); Object y2 = x2;
Console.WriteLine("x2 = " + x2 + "; y2 = " + y2); Console.WriteLine(x2.Equals(y2) ? "x2 == y2" : "x2 != y2"); Console.WriteLine(Object.ReferenceEquals(x2, y2) ?
"ref x2 == ref y2" : "ref x2 != ref y2");
При копировании типов по значению копируются значения объектов, а при копировании ссылочных типов – только ссылки (об этом мы будем говорить далее в этом параграфе). Поэтому на консоли увидим:
x1 = 0; y1 = 0
80