1.3
Переменные
Для описания переменных используется конструкция, формальное описание которой
1
имеет вид:
<тип данных> <идентификатор 1>[=<значение идентификатора 1>]
[, <идентификатор2>[=<значение идентификатора 2>] ...];
Примеры:
double d; int a, b=10; int c = b+7; int d = 0xFF; // d = 255;
Если при описании переменной ей сразу присваивается значение, и данная строчка выполняется несколько раз (например, в цикле), то значение присваивается переменной при каждом выполнении строки.
Переменные могут быть типизированы неявно. В этом случае вместо типа данных указывается ключевое слово var и требуется обязательное применение бло- ка
=<значение идентификатора N>
1
При описании конструкций будут использоваться следующие обозначения:
<текст> описывает блок, вместо которого требуется ввести необходимые данные. Однако выделенные жирным угловые скобки обязательны;
[текст] описывает блок, использование которого необязательно. Однако выделенные жирным квадратные скобки обязательны и не образуют необязательного блока;
… характеризует возможность повторения необязательного блока, внутри которого это обозначение находится.
8
Примеры: var d=1.2; var i=7; var c='h';
Тип переменной определяется по заданному значению, причём для целых зна- чений используется тип int (
или long
, в зависимости от значения), а для веще- ственных – double
. Чтобы указать другие типы, после значения указывается суф- фикс, например, в следующем объявлении var a=1.2F; переменная a будет иметь тип float
Применимы следующие суффиксы:
u или
U
– для типов uint
, ulong
;
l или
L
– для типов long
, ulong
;
ul
, lu и их любые комбинации с учётом регистра – для типa ulong
;
f или
F
– для типов float
;
d или
D
– для типов double
;
m или
M
– для типов decimal
В одной строке нельзя выполнить неявное типизирование двух и более пере- менных, т.е. следующая строка будет ошибочной var a = 5, b = 7;
1.4Константы (литералы) Для описания констант используется конструкция, аналогичная описанию пе- ременных, но перед указанием типа данных указывается модификатор const
. При этом блоки
=<значение идентификатора N>
являются обязательными.
Примеры: const double d=5.3; const int a=7, b=8;
1.5Операторы, используемые при построении выражений Для получения новых значений в любом языке программирования использу- ются выражения, состоящие из операндов и операторов. При построении сложных выражений
требуется учитывать приоритеты операторов, а также порядок вычисле- ния операторов одного приоритета
Операторы языка C#, используемые в выражениях, а также их приоритеты и порядок вычисления, приведены в таблице 1.3.
9
Таблица 1.3 – Операторы С#, используемые при построении выражений
Прио- ритет
Оператор
Описание
1
() обычные скобки или скобки при вызове функций
[] обращение к элементам массива доступ к членам класса
++ постфиксное увеличение
–– постфиксное уменьшение
–> разыменование указателя и доступ к члену new создание объекта и вызов конструктора typeof используется для получения объекта
System.Type для типа checked включение проверки переполнения при выполнении арифме- тических операций и преобразований с данными целого типа unchecked подавление проверки переполнения при выполнении ариф- метических операций и преобразований с данными целого типа
2
++ префиксное увеличение
–– префиксное уменьшение
бинарная инверсия
! отрицание
– унарный минус
+ унарный плюс
& получение адреса
() приведение типа true оператор true false оператор false sizeof получение размера типа данных
3
* умножение
/ деление.
Если оба операнда целочисленные, то будет произ- водиться целочисленное деление; в противном случае – деле- ние с получением вещественного числа
% остаток от деления (в т.ч. дробных чисел)
4
+ сложение
– вычитание
5
<< / >> сдвиг влево / вправо
6
> / < больше / меньше
>= / <= больше или равно / меньше или равно is проверка типа as преобразование типа
7
== , != равно / не равно
8
& логическое «И» (полное)
9
^ логическое «исключающее ИЛИ»
10
| логическое «ИЛИ» (полное)
10
Продолжение таблицы 1.3
Прио- ритет
Оператор
Описание
11
&& логическое «И» (укороченное)
12
|| логическое «ИЛИ» (укороченное)
13
?? поддержка значения null
14
? : условный оператор
15
= присваивание
+=, –=,
*=, /=,
%=, &=,
|=, ^=,
<<=, >>= присваивание с выполнением действия
16
=> лямбда-оператор
1.6
Класс Object
Данный класс является корнем иерархии всех типов и обладает рядом базовых методов, доступных для использования и часто переопределяемых в классах- потомках. К некоторым из этих методов относятся:
Equals(Object obj)
– определяет, равны ли между собой текущий объект и объект obj
Имеется также вариант метода с двумя параметрами
Equals(Object objA, Object objB)
, сравнивающий объекты objA
и objB
(при этом, обращение к методу должно осуществляться через тип данных). Ре- зультатом является логическое значение. Например: int a=6, b=5, c=5; bool d = a.Equals(b); // d = false bool e = int.Equals(b, c); // e = true
ToString()
– возвращает строковое представление объекта. Например: int a=6; string s = a.ToString(); // s = "6"
Также многие классы имеют метод
CompareTo(Object obj)
, позволяющий сравнивать текущий объект с объектом obj
. Метод возвращает целое значение, ко- торое в зависимости от результата сравнения:
меньше нуля, если текущий объект меньше объекта, заданного в параметре;
равно нулю, если объекты равны;
больше нуля, если текущий объект больше объекта, заданного в параметре;
Например: int a=7, b=5, c=5, d=2; int e = b.CompareTo(a); // e = -1 (<0)
11 int f = b.CompareTo(c); // f = 0 (=0) int g = b.CompareTo(d); // g = 1 (>0)
1.7
Класс Math
Класс
Math обеспечивает доступ к ряду математических функций и констант, некоторые из которые приведены в таблице 1.4.
Таблица 1.4 – Некоторые методы и константы класса
Math
Наименование
Описание
Тип результата
Abs(X) абсолютное значение числа
X
тип операнда
Acos(X) арккосинус числа
X
double
Asin(X) арксинус числа
X
double
Atan(X) арктангенс числа
X
double
Atan2(Y,X) арктангенс отношения
Y/X
double
Cos(X) косинус числа
X
double
Cosh(X) гиперболический косинус числа
X
double
DivRem(A,B,out R) целочисленное деление
A/B
. Параметр
R
воз- вращает остаток от деления, например, c = Math.DivRem(7,3,out r)
; // с=2, r=1
целое
Exp(X) возведение числа e в степень
X
double
Log(X[,A]) натуральный логарифм (или логарифм по осно- ванию
А
) числа
X
double
Log10(X) десятичный логарифм числа
X
double
Max(X,Y) наибольшее среди двух чисел
X
и
Y
тип операнда
Min(X,Y) наименьшее среди двух чисел
X
и
Y
тип операнда
Pow(X,Y) возведение числа
X
в степень
Y
double
Round(X[,N][,M]) округление числа
X
до ближайшего целого, а в случае указания числа
N
– до
N
знаков после за- пятой. Параметр
M
может задавать метод округления в случае, если число находится точно по середине между двумя возможными результатами (например, при округлении числа
1,5 до целого) double
Sign(X) знак числа
X
:
-1, если число меньше нуля;
0, если число равно нулю;
1, если число больше нуля int
12
Продолжение таблицы 1.4
Наименование
Описание
Тип результата
Sin(X) синус числа
X
double
Sinh(X) гиперболический синус числа
X
double
Sqrt(X) квадратный корень из числа
X
double
Tan(X) тангенс числа
X
double
Tanh(X) гиперболический тангенс числа
X
double
Truncate(X) целая часть числа
X
double
E константа e double
PI константа
double
Примечание: параметры всех тригонометрических функций задаются в радианах
При обращении к членам класса
Math требуется указывать сам класс, напри- мер, double c = Math.Cos(Math.PI);
1.8
Класс Convert
Класс
Convert предназначен для преобразования значения одного базового типа данных к другому базовому типу данных.
В таблице 1.5 приведены некоторые методы класса.
Таблица 1.5 – Некоторые методы класса
Convert
Наименование
Описание
Тип результата
ChangeType(O,T) возвращает объект с типом
T
и значением, эквивалентным заданному объекту
O
, напри- мер: double d=-2.345; int i = (int)Convert.ChangeType
(d, typeof(int));
// i = -2 тип
T
To<тип>(<значение>) преобразует
<значение>
в тип данных
<тип>
, например: double d=-2.345; string s = Convert.ToString(d);
// s = "-2.345"
<тип>
При обращении к членам класса
Convert требуется указывать сам класс, например, int i = Convert.ToInt32(s);
13
1.9Пространство имён Пространство имён определяет область объявлений, в которой допускается хранить одно множество имён отдельно от другого. По существу, имена, объявлен- ные в одном пространстве имён, не будут вступать в конфликт с аналогичными именами, объявленными в другой области.
Для каждой программы на С# автоматически предоставляется используемое по умолчанию глобальное пространство имён. Но во многих реальных программах приходится создавать собственные пространства имён или же организовать взаимо- действие с другими пространствами имён.
Пространство имён объявляется с помощью ключевого слова namespace
Ниже приведена общая форма объявления пространства имён. namespace <имя> { <члены> }
<имя>
обозначает конкретное имя объявляемого пространства имён, а
<члены>
– все допустимые для C# конструкции (структуры, классы, перечисления и т.д.).
Для подключения пространства имён используется директива using
, фор- мальная запись которой имеет вид: using <имя используемого пространства имен>;
Директива using может не использоваться вообще, однако в этом случае по- требуется каждый раз использовать имя пространства имён при обращении к его членам. Например, если не указать использование пространства имён
System
, то вместо строки: double d = Math.Sin(1); придётся использовать строку double d = System.Math.Sin(1);
Директива using может использоваться для создания псевдонима простран- ства имён. Формальное описание создания псевдонима имеет вид: using <имя псевдонима> = <имя пространства имен>;
Пространства имён
имеют аддитивный характер, т.е. если объявлено два про- странства имён с одинаковым именем, то они складываются в единое пространство имён. Например, если в первом файле имеется описание: namespace NS1
{ class Class1
{
...;
}
}
14 namespace NS1
{ class Class2
{
...;
}
} а во втором файле производится его использование using NS1; то во втором файле доступны оба класса без явного указания пространства имён.
Одно пространство имён может быть вложено в другое, например namespace NS1
{ class Class1
{
...;
} namespace NS2
{ class Class2
{
...;
}
}
}
Если использование пространства имён описано в виде строки using NS1; то это даёт прямой доступ только к классу
Class1
, и для обращения к классу
Class2
потребуется указание полного имени вложенного пространства имён:
Class1 cl1;
NS1.NS2.Class2 cl2;
Вложенное пространство имён также может быть использовано в директиве using
, например: using NS1.NS2;
В этом случае будет прямой доступ только к классу
Class2
, и для обращения к классу
Class1
потребуется явное указание имени пространства имён:
NS1.Class1 cl1;
Class2 cl2;
15
Пространства имён помогают предотвратить конфликты имён, но не устра- нить их полностью. Такой конфликт может, в частности, произойти, когда одно и то же имя объявляется в двух разных пространствах имён и затем предпринимается попытка сделать видимыми оба пространства. Допустим, что два пространства имён содержат класс
MyClass
. Если попытаться сделать видимыми оба пространства имён с помощью директив using
, то имя
MyClass из первого пространства вступит в конфликт с именем
MyClass из второго пространства, обусловив появление ошибки неоднозначности.
Первым способом устранения ошибки может служить явное указание про- странства имён при обращении к классу.
Второй способ подразумевает использование псевдонима пространства имён
::
, формальное описание которого имеет вид:
<псевдоним пространства имен>::<идентификатор> где
<псевдоним пространства имен>
обозначает конкретное имя псевдонима пространства имён, а
<идентификатор>
– имя члена этого пространства. Напри- мер, если имеется описание пространств имён namespace NS1
{ class Class1
{
...;
}
} namespace NS2
{ class Class1
{
...;
}
} и объявлено их совместное использование using pNS1 = NS1; using NS2; то описание объекта класса
Class1
пространства имён
NS1
может быть выполнено следующим образом: pNS1::Class1 cl1;
1
Псевдонимы могут быть назначены не только пространству имён, но и, например, классу:
1
При создании псевдонима само пространство имён не подключается, поэтому строка Class1 cl1; будет считаться допустимой и создаст объект класса Class1 пространства имён NS2
16 using pNS1Class1 = NS1.Class1; pNS1Class1 cl1;
1.10
Типы, допускающие значение null
На основе типов значений могут быть созданы типы, которые могут представ- лять правильный диапазон значений для своего базового типа значений и дополни- тельное пустое значение null
. При описании такого типа после указания базового типа добавляется символ «?», например: int? i = 5; double? d = 6.78;
При совместном использовании базовых типов и типов, допускающих значе- ние null
, могут возникнуть ошибки совместимости, т.к. базовому типу нельзя при- своить значение типов, допускающего значение null
, например: int? i = 5; int j = i; // Ошибка
Данная ошибка может быть исправлена применением метода
GetValueOrDefault()
, который возвращает текущее значение (если оно не null
) или значение по умолчанию для базового типа (если текущее значение null
): int j = i.GetValueOrDefault();
Также данная ошибка может быть исправлена использованием оператора под- держки значения null
«??». Формально формат оператора имеет вид:
<проверяемое значение> ?? <значение, если null>
Если
<проверяемое значение>
имеет значение, отличное от null
, то ре- зультатом работы оператора будет
<проверяемое значение>
, иначе –
<значение, если null>
, например: int? i = null; int j = i ?? 5; // j = 5
Для проверки значения переменной может быть использовано свойство
HasValue
, которое возвращает true
, если текущее значение не null
, и false в противном случае, например: int? i = null; bool b = i.HasValue; // b = false
При вычислении значений выражений если один из операндов имеет значение null
, то и результат будет null
, например: int? i = 5, j = null; int? k = i+j; // k = null
17
2Операторы и конструкции С# В данном разделе рассматриваются операторы, применяемые при построении различных программных конструкций.
2.1Операторы присваивания Основным оператором присваивания является оператор
=
. Формальное описа- ние данного оператора имеет вид:
<идентификатор> = <значение>;
<идентификатор>
должен быть такого типа данных, который может вме- стить в себя присваиваемое значение, или который знает, как обработать присваива- емое значение.
<значение>
может быть числовой константой, переменной или результатом вычисления выражения.
Если требуется изменить значение некоторой переменной с учётом её преды- дущего значения, то могут быть использованы операторы присваивания
+=
,
–=
,
*=
,
/=
,
%=
,
&=
,
|=
,
^=
,
<<=
,
>>=
. Данные операторы выполняют указанную перед символом
=
операцию между операндами, расположенными слева и справа от него, и записывают результат в операнд, указанный слева. Например, выражение a *= b + с;
равносильно выражению a = a*(b + с);
2.2Приведение типов При выполнении операторов присваивания (а также других операторов) в не- которых случаях может выполняться приведение (преобразование) типов, например, во фрагменте программы int a=5; double d=a; во второй строке выполняется неявное (автоматическое) преобразование целого значения в вещественное. Автоматическое преобразование возможно, если:
оба типа совместимы;
диапазон представления чисел целевого типа шире, чем у исходного типа.
Если требуется выполнить явное преобразование значения
переменной или выражения к некоторому типу, то используется конструкция:
(<тип данных>)<преобразуемая величина>
Естественно, что возможность явного преобразования зависит от типа данных и преобразуемой величины.
18
Следует учитывать, что при выполнении выражений также производится пре- образование типов в следующем порядке (т.е. сначала делается первая проверка, при её невыполнении вторая и т.д.):
ЕСЛИ один операнд имеет тип decimal
, TO и второй продвигается к типу decimal
(но если второй операнд имеет тип float или double
, результат будет ошибочным);
ЕСЛИ один операнд имеет тип double
, TO и второй продвигается к типу double
;
ЕСЛИ один операнд имеет тип float
, TO и второй продвигается к типу float
;
ЕСЛИ один операнд имеет тип ulong
, TO и второй продвигается к типу ulong
(но если второй операнд имеет тип sbyte
, short
, int или long
, результат будет ошибочным);
ЕСЛИ один операнд имеет тип long
, TO и второй продвигается к типу long
;
ЕСЛИ один операнд имеет тип uint
, а второй имеет тип sbyte
, short или int
,
ТО оба операнда продвигаются к типу long
;
ЕСЛИ один операнд имеет тип uint
, TO и второй продвигается к типу uint
;
ИНАЧЕ оба операнда продвигаются к типу int
Таким образом, минимальный тип, используемый в выражениях – int
. По- этому во фрагменте программы byte a=100, b=157, c; c = a+b; во второй строке переменные a
и b
будут преобразованы к типу int
, при присваи- вании суммы переменной c
возникнет ошибка и потребуется явное преобразование к byte
(
c = (byte)(a+b);
). Кстати, в этом случае значение c
будет 1.
Если при выполнении арифметических выражений требуется отслеживать пе- реполнение, то может использоваться команда checked
. В этом случае, при пере- полнении возникнет исключительная ситуация. Например, предыдущий пример с контролем переполнения записывается следующим образом: c = checked((byte)(a + b));
Проверяться на переполнение может не только отдельное выражение, но и блок операторов. В этом случае запись контроля переполнения имеет вид: checked
{
<проверяемые операторы>
}
Для отключения контроля переполнения в отдельных выражениях или блоках операторов используется команда unchecked
. Синтаксис команды аналогичен син- таксису команды checked