Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»
Шрифт:
Интервал:
Закладка:
Давайте разберем, как в данном примере организован вывод в методе WriteLine. До сих пор при вызове метода задавалось несколько параметров и использовалась форма вывода данных с подстановкой значений параметров в строку, заданную первым параметром. Здесь же есть только один параметр — это строка, заданная сложным выражением. Операция, многократно применяемая в этом выражении, это сложение " +". Операнды сложения имеют разный тип: левый операнд имеет тип string, правый — арифметический (byte, short, int). в этом случае арифметический тип преобразуется к типу string и выполняется сложение строк (конкатенация). Напомню, при преобразовании арифметического типа к типу string вызывается метод Tostring(), определенный для всех встроенных типов. Результатом этого выражения является строка, она и будет результатом вывода метода WriteLine.
Полагаю, что разбор данного примера и материалы предыдущей лекции, где приводилась иерархия преобразований внутри арифметического типа и обсуждались вопросы выбора реализации перегруженного метода, дают необходимое представление о том, как работает перегрузка операций при вычислении выражений. В деталях, как всегда, может помочь справочная система.
С чего начинается выполнение выражения
Вычисление выражения начинается с выполнения операций высшего приоритета. Первым делом вычисляются выражения в круглых скобках — (ехрr), определяются значения полей объекта — х. у, вычисляются функции — f (х), переменные с индексами — а [i]. Выполнение этих операций достаточно понятно и не нуждается в комментировании. Операции checked и unchecked включают и выключают проверку преобразований арифметического типа в выражениях, которым они предшествуют. О других операциях этой категории скажу чуть подробнее.
Операции "увеличить" и "уменьшить" (increment, decrement)
Операции "увеличить на единицу" и "уменьшить на единицу" могут быть префиксными и постфиксными. К высшему приоритету относятся постфиксные операции х++ и х-. Префиксные операции имеют на единицу меньший приоритет. Главной особенностью как префиксных, так и постфиксных операций является побочный эффект, в результате которого значение х увеличивается (++) или уменьшается (-) на единицу. Для префиксных (++х, — х) операций результатом их выполнения является измененное значение х, постфиксные операции возвращают в качестве результата значение х до изменения. Приведу пример применения этих операций, дополнив метод Express новым фрагментом:
//операции increment и decrement
//Следующее выражение допустимо, но писать подобное никогда не следует
in1 = ++in1 +in1+ in1++;
//in2 = ++in1 + in1++ + in1;
Console.WriteLine(" in1= " + in1);
Обратите внимание, что хотя у постфиксной операции высший приоритет, это вовсе не означает, что при вычислении выражения вначале выполнится операция in1++, затем ++in1, и только потом будет проводиться сложение. Нет, вычисления проводятся в том порядке, в котором они написаны. Поскольку на входе значение in1 было равно 6, то выражение будет вычисляться следующим образом:
7(7) +7 + 7(8),
где в скобках записан побочный эффект операции. Так что консольный вывод даст следующий результат:
in1 = 21
Операциями "увеличить" и "уменьшить" не следует злоупотреблять. Уже оператор, приведенный в нашем фрагменте, сложен для понимания из-за побочного эффекта. Понимаете ли вы, что при изменении порядка записи слагаемых, как это сделано в закомментированном операторе, результат вычисления выражения будет уже не 21, а 22?
Разный приоритет префиксных и постфиксных операций носит условный характер. Эти операции применимы только к переменным, свойствам и индексаторам класса, то есть к выражениям, которым отведена область памяти. В языках C++ и C# такие выражения называются I-value, поскольку они могут встречаться в левых частях оператора присваивания. Как следствие, запись в C# выражения < — х++ > приведет к ошибке. Едва лишь к х слева или справа приписана одна из операций, выражение перестает принадлежать к классу I-value выражений, и вторую операцию приписать уже нельзя.
Операции sizeof и typeof
Операция sizeof возвращает размер значимых типов, заданный в байтах. Пояснения требуют некоторые особенности ее применения. Она должна выполняться только в небезопасных блоках. Поэтому проект, в котором она может использоваться, должен быть скомпилирован с включенным свойством /unsafe. На рис. 6.1 показано, как на странице свойств проекта можно включить это свойство:
Далее необходимо создать небезопасный блок, например, метод класса, помеченный как unsafe, в котором уже можно вызывать эту функцию (операцию). Приведу пример такого метода, созданного в классе Testing:
Рис. 6.1. Включение свойства /unsafe
/// <summary>
/// определение размеров и типов
/// </summary>
unsafe public static void SizeMethod()
{
Console.WriteLine("Размер типа Boolean = " + sizeof (bool));
Console.WriteLine("Размер типа double = " + sizeof(double));
Console.WriteLine("Размер типа char = " + sizeof(System.Char));
int b1=1;
Type t = b1.GetType();
Console.WriteLine("Тип переменной b1: {0}", t);
//Console.WriteLine("Размер переменной b1: {0}", sizeof (t));
}//SizeMethod
В этом примере операция применяется к трем встроенным типам — bool, double, char. Консольный вывод дает в качестве результата значения: 1, 8 и 2. Обращаю внимание на то, что аргументом операции может быть только имя типа. Попытка применить эту операцию к переменной t типа Tуре, имеющей значение System.int32, приводит к ошибке компиляции.
Операция typeof, примененная к своему аргументу, возвращает его тип. И здесь в роли аргумента может выступать имя класса, как встроенного, так и созданного пользователем. Возвращаемый операцией результат имеет тип Tуре, к экземпляру класса применять операцию нельзя, но зато для экземпляра можно вызвать метод GetType, наследуемый всеми классами, и получить тот же результат, что дает typeof с именем данного класса. Такой альтернативный способ получения типа по экземпляру класса int показан в приведенном выше программном фрагменте. А сейчас я приведу фрагмент, где используется вызов операции typeof:
t = typeof(Ciassi);
Console.WriteLine("Тип класса Ciassi: {0}", t);
t = typeof(Testing);
Console.WriteLine("Тип класса Testing: {0}", t);
Как получить подробную информацию о классе?
Пожалуй, следует рассказать не только о том, как можно получить переменную типа Tуре, а и о том, что можно с этой переменной делать.
Этот и последующий раздел прерывают последовательное рассмотрение темы операций языка С#. Полагаю, понимание того, с какой целью выполняются те или иные операции, не менее важно, чем знание самой операции, И я не стал откладывать изложение этого материала на последующие лекции.
Можно ли, зная тип (класс),