Г. Шилдт - Полный справочник по C# (1160789), страница 17
Текст из файла (страница 17)
Во-первых не все типы можно смешивать в одном выражении.Например, не выполняется неявное преобразование значения типа f l o a t или doubleв значение типа decimal. Нельзя также смешивать тип ulong и целочисленный типсо знаком. Чтобы все-таки объединить эти несовместимые типы в одном выражении,необходимо использовать в явном виде операцию приведения типов.Во-вторых, уделите особое внимание последнему правилу. Оно утверждает, что всеоперанды будут преобразованы в значения типа i n t , если не было применено ни одно их предыдущих правил. Следовательно, в выражении все char-, sbyte-, byte-,ushort- и short-значения будут в процессе вычислений преобразованы в значениятипа i n t .
Такое "поголовное" int-преобразование называют целочисленным продвижением типа (integer promotion). Следствием этого алгоритма является то, что результат всех арифметических операций будет иметь тип по "званию" не ниже i n t .Важно понимать, что продвижение типов применяется только к значениям, используемым при вычислении выражения. Например, хотя значение переменной типаbyte внутри выражения будет "подтянуто" до типа i n t , вне выражения эта переменная по-прежнему имеет тип byte. Правило продвижения типов действует только привычислении выражения.Однако продвижение типов может иметь неожиданные последствия. Например,предположим, что арифметическая операция включает два byte-значения. Тогда выполняется следующая последовательность действий.
Сначала byte-операнды"подтягиваются" до типа i n t , затем вычисляется результат операции, который имееттип i n t . Выходит, после выполнения операции над двумя byte-операндами вместоожидаемого byte-результата мы получим int-значение. Именно такая неожиданностьVL может иметь место. А теперь рассмотрим такую программу:// Сюрприз в результате продвижения типов!using System;class PromDemo {public static void Main() {byte b;Глава З.
Типы данных, литералы и переменные77b = 10;b = (byte) (b * b ) ; // Необходимо приведение типов!IConsole.WriteLine("b: "+ b ) ;Кажется странным, что при присвоении результата произведения b * b переменной ь необходимо выполнять операцию приведения типов. Дело в том, что в выражении Ь * b значение переменной b "подтягивается" до типа i n t , т.е. результат выражения b * b представляет собой int-значение, которое нельзя присвоить byteпеременной без приведения типов. Имейте это в виду, если вдруг получите сообщение об ошибке, где сказано о несовместимости типов для выражений, в которых, казалось бы, все в полном порядке.Ситуация подобного рода встречается также при выполнении операций над операндами типа char.
Например, в следующем фрагменте кода также необходимо"возвратить" результат к исходному типу из-за автоматического преобразованияchar-операндов к типу i n t внутри вычисляемого выражения.Ic h a r c h l = f a ' , ch2 = ' b 1 ;chl= (char) ( c h l + c h 2 ) ;Без приведения типов результат сложения операндов c h l и сп2 имел бы тип i n t ,а int-значение невозможно присвоить char-переменной.Продвижение типов также имеет место при выполнении унарных операций(например, с унарным минусом).
Операнды унарных операций, тип которых по диапазону меньше типа i n t (т.е. sbyte-, byte-, s h o r t - и ushort-значения),"подтягиваются" к типу i n t . To же происходит с операндом типа char. Более того,при выполнении операции отрицания uint-значения результат приобретает тип long.Приведение типов в выраженияхОперацию приведения типов можно применить не ко всему выражению, а к конкретной его части. Это позволяет более тонко управлять преобразованием типов привычислении выражения. Рассмотрим, например, следующую программу. Она отображает значения квадратных корней из чисел от 1 до 10. Она также выводит по отдельности целую и дробную части каждого результата.
Для этого в программе используется операция приведения типов, которая позволяет преобразовать результат вызова метода Math. Sqrt () в значение типа i n t .// Приведение типов в выражениях.using System;class CastExpr {public s t a t i c void Main() {double n;;for(n = 1.0; n <= 10; n++) {Console.WriteLine("Квадратный корень из {0} равен {1}",n, Math.Sqrt(n));Console.WriteLine("Целая часть числа: {0}" ,(int)Math.Sqrt(n));Console.WriteLine(78Часть I. Язык С #"Дробная часть числа: {0}",Math.Sqrt(n) - (int) Math.Sqrt(n) );Console.WriteLine();Вот как выглядят результаты выполнения этой программы:Квадратный корень из 1 равен 1Целая часть числа: 1Дробная часть числа: 0Квадратный корень из 2 равен 1.4142135623731Целая часть числа: 1Дробная часть числа: 0.414213562373095Квадратный корень из 3 равен 1.73205080756888Целая часть числа: 1Дробная часть числа: 0.732050807568877Квадратный корень из 4 равен 2Целая часть числа: 2Дробная часть числа: 0Квадратный корень из 5 равен 2.23606797749979Целая часть числа: 2Дробная часть числа: 0.23606797749979Квадратный корень из 6 равен 2.44 948974278318Целая часть числа: 2Дробная часть числа: 0.44*9489742783178Квадратный корень из 7 равен 2.64575131106459Целая часть числа: 2Дробная часть числа: 0.645751311064591Квадратный корень из 8 равен 2.82842712474619Целая часть числа: 2Дробная часть числа: 0.82842712474619Квадратный корень из 9 равен 3Целая часть числа: 3Дробная часть числа: 0Квадратный корень из 10 равен 3.16227766016838Целая часть числа: 3Дробная часть числа: 0.16227766016838Как видно из результатов выполнения программы, приведение значения, возвращаемого методом Math.Sqrt (), к типу int, позволяет получить целую часть значения.
А его дробную часть мы получаем в результате вычисления следующего выражения (если из вещественного числа вычесть его целую часть, то результат даст дробнуючасть исходного числа):I Math.Sqrt(n) - (int) Math.Sqrt(n)Результат этого выражения имеет тип double. Здесь к типу int приводится толькорезультат второго вызова метода Math. Sqr t ().Глава 3. Типы данных, литералы и переменные79Полныйсправочник поОператорыВС# предусмотрен широкий набор операторов, которые дают в руки программисту мощные рычаги управления при создании разнообразнейших выражений иих вычислении. В С# имеется четыре общих класса операторов: арифметические, поразрядные, логические и операторы отношений.
Помимо них в этой главе рассматриваются оператор присвоения и оператор ?. В С# определены также операторы для обработки специальных ситуаций, но их мы рассмотрим после изучения средств, к которым они применяются.Арифметические операторыВ С# определены следующие арифметические операторы.ОператорДействие+Сложение*Умножение/ДелениеВычитание, унарный минус%Деление по модулюДекремент++ИнкрементДействие С#-операторов +, -, * и / совпадает с действием аналогичных операторов в любом другом языке программирования (да и в алгебре, если уж на то пошло).Их можно применять к данным любого встроенного числового типа.Хотя действия арифметических операторов хорошо известны всем читателям, существуют ситуации, которые наверняка потребуют специальных разъяснений.
Преждевсего хочу напомнить, что после применения оператора деления (/) к целому числуостаток будет отброшен. Например, результат целочисленного деления 10/3 будет равен 3. Остаток от деления можно получить с помощью оператора деления по модулю(%). Этот оператор работает практически так же, как в других языках программирования: возвращает остаток от деления нацело. Например, 10 % 3 равно 1. В С# оператор% можно применить как к целочисленным типам, так и типам с плавающей точкой.Например, 10,0 % 3,0 также равно 1. (В языках C/C++ операции деления по модулюприменимы только к целочисленным типам.) Использование оператора деления по^юдулю демонстрируется в следующей программе.// Демонстрация использования оператора %.using System;class ModDemo {public static void Main() {int iresult, irem;double dresult, drem;iresult = 10 / 3;irem = 10 % 3;dresult = 10.0 / 3.0;drem = 10.0 % 3.0;Глава 4.
Операторы81Console.WriteLine("Результат и остаток от деления 1 0 / 3 : " +iresult + " " + irem);Console.WriteLine("Результат и остаток от деления 10.0 / 3.0: " +dresult + " " + drem);(Результат выполнения этой программы таков:Результат и остаток от деления 1 0 / 3 : 3 1Результат и остаток от деления 10.0 / 3.0: 3.33333333333333 1Как видите, оператор % генерирует остаток, равный 1, как при делении целочисленных значений, так и значений с плавающей точкой.Инкремент и декрементОператоры инкремента (++) и декремента (—) увеличивают и уменьшают значениеоперанда на единицу, соответственно. Как будет показано ниже, эти операторы обладают специальными свойствами, которые делают их весьма интересными для рассмотрения.Итак, оператор инкремента выполняет сложение операнда с числом 1, а оператордекремента вычитает 1 из своего операнда.
Это значит, что инструкция1 х = х + 1;аналогична такой инструкции:IТочно так же инструкцияI х = х - 1;аналогична такой инструкции:1Операторы инкремента и декремента могут стоять как перед своим операндом, таки после него. Например, инструкцию| х = х + 1;можно переписать в виде префиксной формыI ++х; // Префиксная форма оператора инкремента.или в виде постфиксной формы:I х++; // Постфиксная форма оператора инкремента.В предыдущем примере не имело значения, в какой форме был применен оператор инкремента: префиксной или постфиксной. Но если оператор инкремента илидекремента используется как часть большего выражения, то форма его примененияимеет важное значение. Если такой оператор применен в префиксной форме, то С#сначала выполнит эту операцию, чтобы операнд получил новое значение, которое затем будет использовано остальной частью выражения.
Если же оператор применен впостфиксной форме, то С# использует в выражении его старое значение, а затем выполнит операцию, в результате которой операнд обретет новое значение. Рассмотримследующий фрагмент кода:| х = 10;У = ++х;82'Часть I. Язык С#В этом случае переменная у будет установлена равной 11. Но если в этом кодепрефиксную форму записи заменить постфиксной, переменная у будет установленашной 10:х = 10;у = х++;ГВ обоих случаях переменная х получит значение 11. Разница состоит лишь в том, вкакой момент она станет равной 11 (до присвоения ее значения переменной у илипосле).Для программиста очень важно иметь возможность управлять временем выполнения операции инкремента или декремента.
Рассмотрим следующую программу, котоая генерирует ряд чисел:/*Демонстрация различия между префиксной ипостфиксной формами оператора ++.*/using System;class PrePostDemo {public static void Main() {int x, y;int i;x = 1;Console.WriteLine("Ряд, построенный с помощью инструкции у = х + х++;");for(i = 0; i < 10;у = х + х++; // постфиксная форма оператора ++Console.WriteLine(у + " " ) ;}Console.WriteLine();х = 1;Console.WriteLine("Ряд, построенный с помощью инструкции у = х + + + х ; " ) ;f o r ( i = 0; i < 10;у = х + ++х; // префиксная форма оператора ++Console.WriteLine(у + " " ) ;}Console.WriteLine();Вот как выглядит результат выполнения этой программы:Ряд, построенный с помощью инструкции у = х + х++;24б8101214Глава 4. Операторы83161820Ряд,3579111315171921построенный с помощью инструкции у = х + ++х;Как видно из результатов работы этой программы, инструкцияI у = х + х++;сначала суммирует значения х и х, после чего присваивает результат переменной у.Только затем она инкрементирует переменную х.