ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3264

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

В этом случае С# вычисляет ЛогическоеВыражение1 и ЛогическоеВыражение2,

а затем смотрит, равны они оба t r u e или нет, чтобы найти, какое значение следует при­ своить переменной Ь. Но может оказаться, что С# выполняет лишнюю работу — ведь если одно из выражений равно f a l s e , то каким бы ни было второе, результат не может быть t r u e в любом случае.

Оператор && позволяет избежать вычисления второго выражения, если после вычисле­ ния первого конечный результат становится очевиден:

b o o l Ь

= (ЛогическоеВыражение1)

&& (ЛогическоеВыражение2);

В этой

ситуации С# вычисляет значение

ЛогическоеВыражение1, и если оно равно

f a l s e , то переменной Ь присваивается значение f a l s e и ЛогическоеВыражение2 не

вычисляется. Если же ЛогическоеВыражение1

равно t r u e , то С# вычисляет Логиче-

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

Оператор

| | работает аналогично, как видно из следующего выражения:

b o o l b =

(ЛогическоеВыражение1)

(ЛогическоеВыражение2);

В этой ситуации С# вычисляет значение ЛогическоеВыражение1, и если оно равно t r u e , то переменной b присваивается значение t r u e и ЛогическоеВыражение2 не вычисляет­ ся. Если же ЛогическоеВыражение1 равно f a l s e , то С# вычисляет ЛогическоеВыражение2 и после этого определяет, какое значение присвоить переменной Ь.

Вы можете называть эти операторы "сокращенным и" и "сокращенным или".

В вычислениях тип результата важен не менее, чем сам результат. Рассмотрим сле­ дующее выражение:

int П ;

П = 5 * 5 + 7;

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

Будучи записано на "языке типов", оно принимает следующий вид: int [=] int * int + int;

Для выяснения типа выражения нужно следовать тому же шаблону, что и при вычис­ лении его значения. Умножение имеет более высокий приоритет, чем сложение. Умно­ жение int на int дает int. Далее идет сложение int и int, что в результате тоже да­ ет int. Итак, вычисление типа приведенного выражения происходит таким образом:

int * int + int int + int

int

В ы ч и с л е н и е т и п а о п е р а ц и и

Выяснение типа выражения происходит в нисходящем направлении посредством вы­ яснения типов подвыражений. Каждое выражение имеет тип, и типы левых и правых ар­ гументов оператора должны соответствовать самому оператору:

t y p e l <ор> t y p e 2 •=> t y p e 3

(Здесь стрелка означает "дает".) Типы t y p e l и t y p e 2 должны быть совместимы с опе­ ратором ор.

80

Часть II. Основы программирования в С#


Большинство операторов могут иметь несколько вариантов. Например, оператор ум­ ножения может быть следующих видов:

int

*

i n t

uint

* u i n t

long

* l o n g

float

* f l o a t

decimal

*

decima l

double

*

doubl e

Таким образом, 2*3 int 6.

•=> i n t

<=> u i n t •=> l o n g •=> f l o a t

<=> d e c i m a l doubl e

использует i n t * i n t версию оператора * и дает в результате

Неявное преобразование типов

Все хорошо, просто и понятно, если умножать две переменные типа i n t или две пе­ ременные типа f l o a t . Но что получится, если типы аргументов слева и справа будут различны? Что, например, произойдет в следующей ситуации:

int nl = 10 ; double d2 = 5 . 0 ;

double dResul t = nl * d2 ;

Во-первых, в C# нет оператора умножения i n t * d o u b l e . C# может просто сгенери­ ровать сообщение об ошибке и предоставить разбираться с проблемой программисту. Однако он пытается понять намерения программиста и помочь ему. У С# есть операторы умножения i n t * i n t и double*double . С# мог бы преобразовать d2 в значение i n t , но такое преобразование привело бы к потере дробной части числа (цифр после десятич­ ной точки). Поэтому вместо этого С# преобразует nl в значение типа doubl e и исполь­ зует оператор умножения double*double . Это действие известно как неявное повы­ шение типа (implicit promotion).

Такое повышение называется неявным, поскольку С# выполняет его автоматически, и является повышением, так как включает естественную концепцию высоты типа. Список

операторов умножения был приведен в порядке повышения— от i n t до double, или от int до decima l — от типа меньшего размера к типу большего размера. Между ти­ пами с плавающей точкой и d e c i m a l неявное преобразование не выполняется. Преоб­ разование из более емкого типа, такого как double, в менее емкий, такой как i n t , на­ зывается понижением (demotion).

Повышение иногда называют преобразованием вверх (up conversion), а понижение —

преобразованием вниз (down conversion).

Неявные понижения запрещены. В таких случаях С# генерирует сообщение об ошибке.

Явное преобразование типа

Но что, если С# ошибается? Если на самом деле программист хотел выполнить целое умножение?

Вы можете изменить тип любой переменной с типом-значением с помощью оператора приведения типа (cast), который представляет собой требуемый тип, заключенный в скобки,

и располагаемый непосредственно перед приводимой переменной или выражением.

Глава 4. Операторы

81


Таким образом, в следующем выражении используется оператор умножения i n t * i n t :

i n t n l

= 1 0

;

d o u b l e

d2 =

5 . 0 ;

d o u b l e n R e s u l t = n l * ( i n t ) d 2 ;

Приведение d2 к типу i n t известно как явное понижение (explicit demotion) или noi нижающее приведение (downcast). Понижение является явным, поскольку программю! явно объявил о своих намерениях.

Вы можете осуществить приведение между двумя любыми типами-значениями, нем висимо от их взаимной высоты.

Избегайте неявного преобразования типов. Делайте все изменения типов! значений явными с помощью оператора приведения.

Оставьте логику в покое

С# не позволяет преобразовывать другие типы в тип b o o l или выполнять преобразо­ вание типа b o o l в другие типы.

Т и п ы при присваивании

Все сказанное о типах выражений применимо и к оператору присваивания.

Случайные несоответствия типов, приводящие к генерации сообщений об ошибках, обычно происходят в операторах присваивания, а не в точке действи­ тельного несоответствия.

Рассмотрим следующий пример умножения:

i n t

n l = 1 0 ;

i n t

П2 = 5 . 0 * n l ;

Вторая строка этого примера приведет к генерации сообщения об ошибке, связанной с несоответствием типов, но ошибка произошла при присваивании, а не при умножении. Вот что произошло: для того чтобы выполнить умножение, С# неявно преобразовал nl в тип d o u b l e . Затем С# выполнил умножение двух значений типа d o u b l e , получив в результате значение того же типа d o u b l e .

Типы левого и правого аргументов оператора присваивания должны совпадать, но тип левого аргумента не может быть изменен. Поскольку С# не может неявно понизить тип выражения, компилятор генерирует сообщение о том, что он не может неявно пре­ образовать тип d o u b l e в i n t .

При использовании явного приведения никаких проблем не возникнет:

i n t n l = 1 0 ;

i n t n2 = ( i n t ) ( 5 . 0 * n l ) ;

(Скобки необходимы, потому что оператор приведения имеет очень высокий приоритет.) Такой исходный текст вполне работоспособен, так как явное понижение разрешено. Здесь значение nl будет повышено до d o u b l e , выполнено умножение, а результат типа d o u b l e будет понижен до i n t . Однако в этой ситуации надо подумать о душевном здо­ ровье программиста, поскольку написать просто 5*п1 было бы проще как для програм­ миста, так и для С#.

82

Часть II. Основы программирования в С#


Большинство операторов имеют два аргумента, меньшинство —- один. И только один оператор — тернарный — имеет три аргумента. Лично я считаю, что это ненужная экзо­ тика. Вот формат этого оператора:

Выражение типа boo l ? Выражение1 : Выражение2

А это пример его применения: int а = 1;

int Ь = 2 ;

int nMax = (a>b) ? а : Ь;

Если а больше Ь (условие в скобках), значение выражения равно а. Если а не больше Ь, значение выражения равно Ь.

Выражения 1 и 2 могут быть любой сложности, но это должны быть истинные выра­ жения— они не могут содержать объявлений или других инструкций, не являющихся выражениями.

Тернарный оператор непопулярен3 по следующим причинам.

Он не является необходимым. Использование оператора if, описанного в гла­ ве 5, "Управление потоком выполнения", дает тот же эффект, и его легче понять.

На тернарный оператор накладываются дополнительные ограничения. На­ пример, выражения 1 и 2 должны быть одного и того же типа. Это приводит к следующему:

in t

а =

1 ;

double b = 0 . 0 ;

in t

nMax

= (a>b) ? а : b;

Такой исходный текст не будет компилироваться, несмотря на то что в конечном итоге nMax будет иметь значение а. Поскольку а и b должны быть одного и того же типа, а будет повышено до double, чтобы соответствовать Ь. Тип результи­

рующего значения оператора

?:

оказывается double, и этот тип должен быть

понижен до i n t перед присваиванием:

in t

а =

1 ;

 

 

 

double b

= 0 . 0 ;

 

 

 

i n t

nMax;

 

 

 

//

Можно поступить

так:

 

 

nMax =

( i n t ) ( (a>b)

? а

:

b) ;

//. . . или так:

nMax = (a>b) ? а : ( i n t ) b ;

Увидеть тернарный оператор в реальной программе — большая редкость.

3 Непопулярность этого оператора относится к С#, программистами на С и С++ он употребля­ ется достаточно часто и не вызывает никаких отрицательных эмоций. — Примеч. ред.

Глава 4. Операторы

83


Глава 5

Управление потоком выполнения

>Что делать, если...

>Цикл w h i l e

>Цикл for

>Конструкция s w i t c h

ассмотрим следующую простую программу:

 

 

using System;

 

 

 

 

namespace

HelloWorl d

 

 

 

(

 

 

 

 

 

public

c l a s s Program

 

 

 

{

 

 

 

 

 

//

Стартовая точка

программы

 

 

s t a t i c

v o i d M a i n ( s t r i n g [ ] args )

 

 

{

 

 

 

 

 

 

//

Приглашение

для ввода имени

 

 

C o n s o l e . W r i t e L i n e ( " В в е д и т е

ваше

и м я : " ) ;

 

//

Считывание

введенного имени

 

 

s t r i n g sName =

C o n s o l e . R e a d L i n e ( ) ;

 

//

Приветствие

с использованием

введенного имени

 

C o n s o l e . W r i t e L i n e ( " П р и в е т ,

" + sName);

 

//

Ожидание подтверждения

пользователя

 

Console . WriteLine("Нажмит е <Enter> для "

 

 

 

"завершения

программы ... ") ;

 

C o n s o l e . R e a d ( ) ;

 

 

Толку от этой программы, помимо иллюстрации некоторых фундаментальных мо­ ментов программирования С#, очень мало. Она просто возвращает вам то, что вы ввели. Вы можете представить более сложный пример программы, в которой выполняются не­ которые вычисления над введенными данными и генерируется какой-то более сложный вывод на экран (иначе для чего проводить вычисления?...), но и эта программа будет очень ограничена в своей функциональности.

Одним из ключевых элементов любого компьютерного процессора является его воз­ можность принимать решения. Под выражением "принимать решения" имеется в виду, ! что процессор может пустить поток выполнения команд по одному или другому пути