Страница 18 из 188
ЕСЛИ один операнд имеет тип long, ТО и второй операнд продвигается к типу long.
ЕСЛИ один операнд имеет тип uint, а второй — тип sbyte, short или int, ТО оба операнда продвигаются к типу long.
ЕСЛИ один операнд имеет тип uint, ТО и второй операнд продвигается к типу uint.
ИНАЧЕ оба операнда продвигаются к типу int.*Относительно правил продвижения типов необходимо сделать ряд важных замечаний. Во-первых, не все типы могут смешиваться в выражении. В частности, неявноепреобразование типа float или double в тип decimal невозможно, как, впрочем,и смешение типа ulong с любым целочисленным типом со знаком. Для смешенияэтих типов требуется явное их приведение.
Во-вторых, особого внимания требует последнее из приведенных выше правил. Оногласит: если ни одно из предыдущих правил не применяется, то все операнды продвигаются к типу int. Следовательно, все значения типа char, sbyte, byte, ushortи short продвигаются к типу int в целях вычисления выражения. Такое продвижениетипов называется целочисленным. Это также означает, что результат выполнения всехарифметических операций будет иметь тип не ниже int.
Следует иметь в виду, что правила продвижения типов применяются только к значениям, которыми оперируют при вычислении выражения. Так, если значение переменной типа byte продвигается к типу int внутри выражения, то вне выражения этапеременная по-прежнему относится к типу byte. Продвижение типов затрагиваеттолько вычисление выражения.
Но продвижение типов может иногда привести к неожиданным результатам. Если,например, в арифметической операции используются два значения типа byte, то происходит следующее. Сначала операнды типа byte продвигаются к типу int. А затемвыполняется операция, дающая результат типа int. Следовательно, результат выполнения операции, в которой участвуют два значения типа byte, будет иметь тип int.Но ведь это не тот результат, который можно было бы с очевидностью предположить.Рассмотрим следующий пример программы.// Пример неожиданного результата продвижения типов!using System;class PromDemo { static void Main() { byte b; b = 10; b = (byte) (b * b); // Необходимо приведение типов!! Console.WriteLine("b: "+ b); }}
Как ни странно, но когда результат вычисления выражения bb присваивается обратно переменной b, то возникает потребность в приведении к типу byte! Объясняется этотем, что в выражении bb значение переменной b продвигается к типу int и поэтому неможет быть присвоено переменной типа byte без приведения типов. Имейте это обстоятельство в виду, если получите неожиданное сообщение об ошибке несовместимоститипов в выражениях, которые, на первый взгляд, кажутся совершенно правильными.
Аналогичная ситуация возникает при выполнении операций с символьными операндами. Например, в следующем фрагменте кода требуется обратное приведениек типу char, поскольку операнды ch1 и ch2 в выражении продвигаются к типу int.char ch1 = 'a', ch2 = 'b';ch1 = (char) (ch1 + ch2);
Без приведения типов результат сложения операндов ch1 и ch2 будет иметь типint, и поэтому его нельзя присвоить переменной типа char.
Продвижение типов происходит и при выполнении унарных операций, напримерс унарным минусом. Операнды унарных операций более мелкого типа, чем int(byte,sbyte, short и ushort), т.е. с более узким диапазоном представления чисел, продвигаются к типу int. То же самое происходит и с операндом типа char. Кроме того,если выполняется унарная операция отрицания значения типа uint, то результат продвигается к типу long.Приведение типов в выражениях
Приведение типов можно применять и к отдельным частям крупного выражения.Это позволяет точнее управлять преобразованиями типов при вычислении выражения. Рассмотрим следующий пример программы, в которой выводятся квадратныекорни чисел от 1 до 10 и отдельно целые и дробные части каждого числового результата. Для этого в данной программе применяется приведение типов, благодаря которому результат, возвращаемый методом Math.Sqrt(), преобразуется в тип int.// Пример приведения типов в выражениях.using System;class CastExpr { static 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("Дробная часть числа: (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.44948974278318Целая часть числа: 2Дробная часть числа: 0.449489742783178Квадратный корень из 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 позволяет получить целую часть числа. Так, в выраженииMath.Sqrt(n) - (int) Math.Sqrt(n)
приведение к типу int дает целую часть числа, которая затем вычитается из всегочисла, а в итоге получается дробная его часть. Следовательно, результат вычисленияданного выражения имеет тип double. Но к типу int приводится только значение,возвращаемое вторым методом Math.Sqrt().
ГЛАВА 4. Операторы
В языке C# предусмотрен обширный ряд операторов,предоставляющих программирующему возможностьполного контроля над построением и вычислениемвыражений. Большинство операторов в С# относится к следующим категориям: арифметические, поразрядные, логические и операторы отношения. Все перечисленные категорииоператоров рассматриваются в этой главе. Кроме того, в C#предусмотрен ряд других операторов для особых случаев,включая индексирование массивов, доступ к членам классаи обработку лямбда-выражений. Эти специальные операторы рассматриваются далее в книге вместе с теми средствами, в которых они применяются.Арифметические операторы
Арифметические операторы, представленные в языкеС#, приведены ниже.ОператорДействие+Сложение-Вычитание, унарный минус*Умножение/Деление%Деление по модулю—Декремент++Инкремент
Операторы +, -, * и / действуют так, как предполагает их обозначение. Их можноприменять к любому встроенному числовому типу данных.
Действие арифметических операторов не требует особых пояснений, за исключением следующих особых случаев. Прежде всего, не следует забывать, что когда оператор/ применяется к целому числу, то любой остаток от деления отбрасывается; например,результат целочисленного деления 10/3 будет равен 3. Остаток от этого деления можнополучить с помощью оператора деления по модулю (%), который иначе называетсяоператором вычисления остатка. Он дает остаток от целочисленного деления. Например, 10 % 3 равно 1. В C# оператор % можно применять как к целочисленным типамданных, так и к типам с плавающей точкой. Поэтому 10.0 % 3.0 также равно 1.В этом отношении C# отличается от языков С и C++, где операции деления по модулюразрешаются только для целочисленных типов данных. В приведенном ниже примерепрограммы демонстрируется применение оператора деления по модулю.// Продемонстрировать применение оператора %.using System;class ModDemo { 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; Console.WriteLine("Результат и остаток от деления 10/3: " + iresult + " " + irem); Console.WriteLine("Результат и остаток от деления 10.0 / 3.0: " + dresult + " " + drem); }}