Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Другие элементы меню 1 страница




Else

Else

Try

Try

Do

Else

Else

Else

Set

Else

Else

Set

Else

Try

Else

Else

Else

Else

Else

Else

Do

Else

Set

Else

Else

Else

Else

Else

Do

Do

Else

Else

{

int u= 7; int v= u+y;

Console.WriteLine("y= {0}; u={1}; v={2}",

y,u,v);

}

//Console.WriteLine("y= {0}; u={1}; v={2}",y,u,v);

 

//Локальные переменные не могут быть статическими.

//static int Count = 1;

//Ошибка: использование sum до объявления

//Console.WriteLine("x= {0}; sum ={1}",

// x,sum);

int i; long sum =0;

for (i=1; i<x; i++)

{

//ошибка: коллизия имен: y

//float y = 7.7f;

sum +=i;

}

Console.WriteLine("x= {0}; sum ={1}",

x,sum);

}// ScopeVar

Заметьте, в теле метода встречаются имена полей, аргументов и локальных переменных. Эти имена могут совпадать. Например, имя x имеет поле класса и формальный аргумент метода. Это допустимая ситуация. В языке C# разрешено иметь локальные переменные с именами, совпадающими с именами полей класса – в нашем примере таким является имя y, однако запрещено иметь локальные переменные, имена которых совпадают с именами формальных аргументов. Этот запрет распространяется не только на внешний уровень процедурного блока, что вполне естественно, но и на все внутренние блоки.

В процедурный блок вложены два блока, порожденные оператором if. В каждом из этих блоков объявлены переменные с одинаковыми именами u и v. Это корректные объявления, поскольку время существования и области видимости этих переменных не пересекаются. Итак, для не вложенных блоков разрешено объявление локальных переменных с одинаковыми именами. Заметьте также, что переменные u и v перестают существовать после выхода из блока, так что операторы печати, расположенные внутри блока работают корректно, а оператор печати вне блока приводит к ошибке, – u и v здесь не видимы, кончилось время их жизни. По этой причине оператор закомментирован.

Выражение, проверяемое в операторе if, зависит от значения поля name. Значение поля глобально для метода и доступно всюду, если только не перекрывается именем аргумента (как в случае с полем x) или локальной переменной (как в случае с полем y).

Во многих языках программирования разрешено иметь локальные статические переменные, у которых область видимости определяется блоком, но время их жизни совпадает со временем жизни проекта. При каждом повторном входе в блок такие переменные восстанавливают значение, полученное при предыдущем выходе из блока. В языке C# статическими могут быть только поля, но не локальные переменные. Незаконная попытка объявления static переменной в процедуре ScopeVar закомментирована. Попытка использовать имя переменной в точке, предшествующей ее объявлению также незаконна и закомментирована.

Глобальные переменные уровня процедуры. Существуют ли?

Поскольку процедурный блок имеет сложную структуру с вложенными внутренними блоками, то и здесь возникает тема глобальных переменных. Переменная, объявленная во внешнем блоке, рассматривается как глобальная по отношению к внутренним блокам. Во всех известных мне языках программирования во внутренних блоках разрешается объявлять переменные с именем, совпадающим с именем глобальной переменной. Конфликт имен разрешается за счет того, что локальное внутреннее определение сильнее внешнего. Поэтому область видимости внешней глобальной переменной сужается и не распространяется на те внутренние блоки, где объявлена переменная с подобным именем. Внутри блока действует локальное объявление этого блока, при выходе восстанавливается область действия внешнего имени. В языке C# этот гордиев узел конфликтующих имен разрублен, – во внутренних блоках запрещено использование имен, совпадающих с именем, использованном во внешнем блоке. В нашем примере незаконная попытка объявить во внутреннем блоке уже объявленное имя y закомментирована.

Обращаю внимание, подобные решения, принятые разработчиками языка C#, не только упрощают жизнь разработчикам транслятора. Они способствуют повышению эффективности программ, а самое главное, повышают надежность программирования на C#.

Отвечая на вопрос, вынесенный в заголовок, следует сказать, что глобальные переменные на уровне процедуры в языке C# конечно же есть, но нет конфликта имен между глобальными и локальными переменными на этом уровне. Область видимости глобальных переменных процедурного блока распространяется на весь блок, в котором они объявлены, начиная от точки объявления, и не зависит от существования внутренних блоков. Когда говорят, что в C# нет глобальных переменных, то, прежде всего, имеют в виду их отсутствие на уровне модуля. Уже во вторую очередь речь идет об отсутствии конфликтов имен на процедурном уровне.

Константы

Константы C# могут появляться, как обычно, в виде литералов и именованных констант. Вот пример константы, заданной литералом, стоящей в правой части оператора присваивания:

y = 7.7f;

Значение константы “7.7f” является одновременно ее именем, оно же позволяет однозначно определить тип константы. Заметьте, иногда, как в данном случае, приходится добавлять к значению специальные символы для точного указания типа. Я не буду останавливаться на этих подробностях. Если возникает необходимость уточнить, как записываются литералы, то достаточно получить справку по этой теме. Делается все также как и в языке C++.

Всюду, где можно объявить переменную, можно объявить и именованную константу. Синтаксис объявления схож. В объявление добавляется модификатор const, инициализация констант обязательна и не может быть отложена. Инициализирующее выражение может быть сложным, важно, чтобы оно было вычислимым в момент его определения. Вот пример объявления констант:

/// <summary>

/// Константы

/// </summary>

public void Constants()

{

const int SmallSize = 38, LargeSize =58;

const int MidSize = (SmallSize + LargeSize)/2;

const double pi = 3.141593;

//LargeSize = 60; //Значение константы нельзя изменить.

Console.WriteLine("MidSize= {0}; pi={1}",

MidSize, pi);

}// Constants

Вариант 1

10. Отметьте правильные объявления переменной:

q int x;

q int x(77);

q int x =77;

q int x = new int (77);

q int x = new int ();

11. Отметьте истинные высказывания:

q глобальные переменные в C# могут быть объявлены только в пространстве имен;

q поля класса играют роль глобальных переменных для методов класса;

q имя локальной переменной не должно совпадать ни с именем аргумента метода, ни с именем поля класса;

q все имена переменных внутри одной процедуры уникальны;

q в непересекающихся блоках процедуры допускаются одинаковые имена переменных.

12. Отметьте правильные объявления константы:

q const double size = 55;

q const int size = 55;

q const byte size = 55;

q const double size = 5.5;

q const size = 55;

Вариант 2

13. Отметьте истинные высказывания:

q в момент объявления все переменные должны быть инициализированы;

q при объявлении переменных их не следует инициализировать, поскольку используется инициализация по умолчанию;

q объявление переменных допускает отложенную инициализацию;

q инициализирующее выражение должно быть константным выражением;

q при объявлении переменных в объектном стиле не допускается задавать в конструкторе инициализирующее выражение.

14. Отметьте истинные высказывания:

q Поля класса могут быть объявлены как static. Время жизни и область видимости для них определяется классом.

q Все экземпляры класса используют одно и то же поле, если оно объявлено как static.

q Переменные процедуры могут быть объявлены как static. Для таких переменных при каждом входе в процедуру восстанавливается значение, полученное при предыдущем выходе.

q Область существования переменной, объявленной в процедуре, распространяется на весь процедурный блок.

15. Какие из выражений являются правильно построенными константами:

q 5+7;

q 0,5

q 0.5e-7;

q 5.5f;

Вариант 3

13. Отметьте правильные объявления:

q int x, double y;

q int x, y, z = 77;

q int x =77, y = new int();

q int x = 4, y, z = x +4;

q int x = 4; int y, z = x+4;

14. Отметьте истинные высказывания:

q глобальная переменная процедурного блока видима во всех внутренних блоках этой процедуры;

q глобальная переменная процедурного блока видима во всех внутренних блоках этой процедуры за исключением тех внутренних блоков, в которых объявлена локальная переменная с тем же именем;

q область видимости локальной переменной начинается от точки объявления до конца процедурного блока;

q глобальные и локальные имена переменных внутри одной процедуры могут совпадать.

15. Отметьте правильные объявления константы:

q double const size = 75.5f;

q const double size = 53.5f;

q const byte size = 055;

q const size = 33;

 

 

 

Лекция 6. Выражения. Операции в выражениях

Построение выражений. Операции и их приоритеты. Описание операций.

Ключевые слова: выражение; приоритет операций; таблица приоритетов; перегрузка операций; операция увеличить; операция уменьшить; операция sizeof; операция typeof; метод Parse; операция new; арифметические операции; операции отношения; операции сдвига; логические выражения; логические операции; условное выражение; приведение к типу.

Выражения

Выражения строятся из операндов – констант, переменных, функций, – объединенных знаками операций и скобками. При вычислении выражения определяется его значение и тип. Эти характеристики выражения однозначно определяются значениями и типами операндов, входящих в выражение, и правилами вычисления выражения. Правила задают:

приоритет операций,

для операций одного приоритета порядок применения – слева направо или справа налево;

преобразование типов операндов и выбор реализации для перегруженных операций;

тип и значение результата выполнения операции над заданными значениями операндов определенного типа.

Программист, записывающий выражение должен знать, по каким правилам оно будет вычисляться. Сложность в том, что эти правила, начиная с приоритета операций, варьируются от языка к языку. Давайте посмотрим, как это делается в C#.

Приоритет и порядок выполнения операций

Большинство операций в языке C#, их приоритет и порядок наследованы из языка C++. Однако есть и различия, например, нет операции «,», позволяющей вычислять список выражений, добавлены уже упоминавшиеся операции checking и unchecking, применимые к выражениям.

Как это обычно делается, приведем таблицу приоритетов операций, в каждой строке которой собраны операции одного приоритета, а строки следуют в порядке приоритетов, от высшего к низшему.

Таблица 6-1. Приоритеты операций языка C#

Приоритет Категория Операции Порядок
  Первичные (expr) x.y f(x) a[x] x++ x-- new sizeof(t) typeof(t) checked(expr) unchecked(expr) Слева направо
  Унарные + -! ~ ++x --x (T)x Слева направо
  Мультипликативные (Умножение) * / % Слева направо
  Аддитивные (Сложение) + - Слева направо
  Сдвиг << >> Слева направо
  Отношения, проверка типов < > <= >= is as Слева направо
  Эквивалентность ==!= Слева направо
  Логическое И (AND) & Слева направо
  Логическое исключающее ИЛИ (XOR) ^ Слева направо
  Логическое ИЛИ (OR) | Слева направо
  Условное И && Слева направо
  Условное ИЛИ || Слева направо
  Условное выражение ?: Справа налево
  Присваивание = *= /= %= += -= <<= >>= &= ^= |= Справа налево

Перегрузка операций

Под перегрузкой операции понимается существование нескольких реализаций одной и той же операции. Большинство операций языка C# перегружены – одна и та же операция может применяться к операндам различных типов. Поэтому прежде, чем выполнять операцию, идет поиск реализации, подходящей для данных типов операндов. Замечу, что операции, как правило, выполняются над операндами одного типа. Если же операнды разных типов, то предварительно происходит неявное преобразование типа операнда. Оба операнда могут быть одного типа, но преобразование типов может все равно происходить по той причине, что для заданных типов нет соответствующей перегруженной операции. Такая ситуация достаточно часто возникает на практике, поскольку, например, операция сложения не определена для младших подтипов арифметического типа. Приведу начальный фрагмент процедуры Express, предназначенной для анализа выражений:

/// <summary>

/// Анализ выражений

/// </summary>

public void Express()

{

//перегрузка операций

byte b1 =1, b2 =2, b3;

short sh1;

int in1;

//b3 = b1 + b2; //ошибка: результат типа int

b3 = (byte)(b1+b2);

//sh1 = b1 + b2; //ошибка: результат типа int

sh1 = (short)(b1+b2);

in1 = b1+ b2 + sh1;

Console.WriteLine("b3= " + b3 + " sh1= "

+ sh1 +" in1= " + in1);

 

}// Express

Прокомментирую этот фрагмент. Начну с первого закомментированного оператора присваивания b3 = b1+b2;. Выражение здесь простейшее, включает одну бинарную операцию сложения. Оба операнда имеют тип byte, казалось бы, и результат должен быть типа byte и без помех присвоен переменной b3. Однако это не так. Для данных типа byte нет перегруженной реализации сложения. Ближайшей операцией является сложение целых типа int. Поэтому оба операнда преобразуются к типу int, выполняется операция сложения, результат имеет тип int и не может быть неявно преобразован в тип byte, – возникает ошибка еще на этапе компиляции. Корректная запись показана в следующем операторе. Аналогичная ситуация возникает, когда в левой части оператора стоит переменная типа short, – и здесь необходимо явное приведение к типу. Этого приведения не требуется, когда в левой части стоит переменная типа int.

Давайте разберем, как в данном примере организован вывод в методе WriteLine. До сих пор при вызове метода задавалось несколько параметров, и использовалась форма вывода данных с подстановкой значений параметров в строку, заданную первым параметром. Здесь же есть только один параметр – это строка, заданная сложным выражением. Операция, многократно используемая в этом выражении, это сложение «+». Операнды сложения имеют разный тип: левый операнд имеет тип string, правый – арифметический (byte, short, int). В этом случае арифметический тип преобразуется к типу string и выполняется сложение строк (конкатенация). Напомню, при преобразовании арифметического типа к типу string вызывается метод ToString(), определенный для всех встроенных типов. Результатом этого выражения будет строка, она и будет результатом вывода метода WriteLine.

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

С чего начинается выполнение выражения

Вычисление выражения начинается с выполнения операций высшего приоритета. Первым делом вычисляются выражения в круглых скобках – (expr), определяются значения полей объекта – x.y, вычисляются функции – f(x), переменные с индексами – a[i]. Выполнение этих операций достаточно понятно и не нуждается в комментировании. Операции checked и unchecked включают и выключают проверку преобразований арифметического типа в выражениях, которым они предшествуют. О других операциях этой категории скажу чуть подобнее.

Операции «увеличить» и «уменьшить» (increment, decrement)

Операции увеличить на единицу и уменьшить на единицу могут быть префиксными и постфиксными. К высшему приоритету относятся постфиксные операции x++ и x--. Префиксные операции имеют на единицу меньший приоритет. Главной особенностью как префиксных, так и постфиксных операций является побочный эффект, в результате которого значение x увеличивается (++) или уменьшается (--) на единицу. Для префиксных (++x, --x) операций результатом их выполнения является измененное значение x, постфиксные операции возвращают в качестве результата операции значение x до изменения. Приведу пример применения этих операций, дополнив метод Express новым фрагментом:

//операции increment и decrement

//Следующее выражение допустимо,

//но писать подобное никогда не следует

in1 = ++in1 +in1+ in1++;

//in1 = ++in1 + in1 + in1++;

Console.WriteLine(" in1= " + in1);

Обратите внимание, хотя у постфиксной операции высший приоритет, это вовсе не означает, что при вычислении выражения вначале выполнится операция in1++, затем ++in1, и только потом будет проводиться сложение. Нет, вычисления проводятся в том порядке, в котором они написаны. Поскольку на входе значение in1 было равно 6, то выражение будет вычисляться следующим образом:

7(7) + 7 + 7(8),

где в скобках записан побочный эффект операции. Так что консольный вывод даст следующий результат:

in1 = 21

Операциями «увеличить» и «уменьшить» не следует злоупотреблять. Уже оператор, приведенный в нашем фрагменте, сложен для понимания из-за побочного эффекта. Понимаете ли вы, что при изменении порядка записи слагаемых, как это сделано в закомментированном операторе, результат вычисления выражения будет уже не 21, а 22.

Разный приоритет префиксных и постфиксных операций носит условный характер. Эти операции применимы только к переменным, свойствам и индексаторам класса, то есть к выражениям, которым отведена область памяти. В языках C++ и C# такие выражения называются l-value, поскольку они могут встречаться в левых частях оператора присваивания. Как следствие, запись в C# выражения < --x++ > приведет к ошибке. Как только к x слева или справа приписана одна из операций, выражение перестает принадлежать к классу l-value выражений и вторую операцию приписать уже нельзя.

Операции sizeof и typeof

Операция sizeof возвращает размер значимых типов, заданный в байтах. Пояснения требуют некоторые особенности ее применения. Она должна выполняться только в небезопасных блоках. Поэтому проект, в котором она может использоваться, должен быть скомпилирован с включенным свойством / unsafe. На рис. 6.1 показано, как на странице свойств проекта можно включить это свойство:

Рис. 6.1. Включение свойства /unsafe

Далее необходимо создать небезопасный блок, например, метод класса, помеченный как unsafe, в котором уже можно вызывать эту функцию (операцию). Приведу пример такого метода, созданного в классе Testing:

/// <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 типа Type, имеющей значение System.Int32 приводит к ошибке компиляции.

Операция typeof, примененная к своему аргументу, возвращает его тип. И здесь в роли аргумента может выступать имя класса, как встроенного, так и созданного пользователем. Возвращаемый операцией результат имеет тип Type. К экземпляру класса применять операцию нельзя, но зато для экземпляра можно вызвать метод GetType, наследуемый всеми классами, и получить тот же результат, что дает typeof с именем данного класса. Такой альтернативный способ получения типа по экземпляру класса int показан в приведенном выше программном фрагменте. А сейчас приведу фрагмент, где используется вызов операции typeof:

t = typeof (Class1);

Console.WriteLine("Тип класса Class1: {0}", t);

t = typeof (Testing);

Console.WriteLine("Тип класса Testing: {0}", t);

Как получить подробную информацию о классе?

Пожалуй, более интересно рассказать не только о том, как можно получить переменную типа Type, а и то, что можно с этой переменной делать.

Этот и последующий раздел прерывают последовательное рассмотрение темы операций языка C#. Полагаю, понимание того, с какой целью выполняются те или иные операции, не менее важно, чем знание самой операции, И я не стал откладывать изложение этого материала на последующие лекции.

Можно ли, зная тип (класс) получить подробную информацию обо всех методах и полях класса? Ясно, что такая информация может быть весьма полезной, если класс поставлен сторонней фирмой. Оказывается, это сделать нетрудно. Вся необходимая информация содержится в метаданных, поставляемых вместе с классом. Процесс получения метаданных называется отражением (reflection). Об отражении и метаданных уже говорилось в первой вводной лекции, и эта тема будет обсуждаться и далее. А сейчас я приведу пример, демонстрирующий получение подробной информации о методах и полях класса. Первым делом следует упростить в проекте использование классов пространства имен Reflection, добавив в начало проекта предложение

using System.Reflection;

В класс Testing я добавил существенно расширенный вариант метода WhoIsWho, который уже появлялся в наших примерах. Вот текст новой версии этой процедуры:

/// <summary>

/// Подробная информация о классе объекта, его значении

/// методах класса, всех членов класса

/// </summary>

/// <param name="name"> имя объекта </param>

/// <param name="any"> объект любого типа </param>

public void WhoIsWho(string name, object any)

{

Type t = any.GetType();

Console.WriteLine("Тип {0}: {1}, значение: {2}",

name, any.GetType(), any.ToString());

Console("Методы класса:");

MethodInfo[] ClassMethods = t.GetMethods();

foreach (MethodInfo curMethod in ClassMethods)

{

Console.WriteLine(curMethod);

}

Console.WriteLine("Все члены класса:");

MemberInfo[] ClassMembers = t.GetMembers();

foreach (MemberInfo curMember in ClassMembers)

{

Console.WriteLine(curMember.ToString());

}

}// WhoIsWho

Коротко прокомментирую эту процедуру. Вначале создается переменная t типа Type. Значением этой переменной будет тип аргумента, переданного в процедуру в качестве значения параметра any. Напомню, any имеет базовый тип object и потому метод может быть вызван с аргументом, роль которого может играть выражение любого типа. Далее вызываются методы переменной tGetMethods() и GetMembers(). Эти методы соответственно возвращают в качестве значений массивы элементов классов MethodInfo и MemberInfo. Эти классы содержатся в пространстве имен Reflection, они содержат информацию в первом случае о методах класса, во втором – о полях и методах класса, заданного переменной t. В пространстве имен Reflection содержатся и другие классы, имеющие методы и свойства, полезные для получения дополнительной информации об исследуемом классе. Но я не буду сейчас столь подробно развивать эту тему.

В процедуре Main дважды вызывается процедура WhoIsWho. В первом вызове ее аргументом является выражение типа double, во втором – сам объект ts, вызывающий метод:

ts.WhoIsWho("2+2.5", 2+2.5);

ts.WhoIsWho("ts", ts);

И класс double, и созданный в этом проекте класс Testing имеют довольно много методов. Имеют они и свойства. Процедура WhoIsWho выдаст подробную информацию обо всех элементах этих классов. Результаты консольного вывода, полученного при двух вызовах этой процедуры, показаны на рис. 6.2:

Рис. 6.2. Информация о классах int и Testing, полученная в процедуре WhoIsWho

Рассмотрим выводимую информацию о классах. Для созданного в проекте класса Testing отображается информация о полях и методах как собственных, так и наследуемых от общего родителя – класса object. Заметьте, отображается информация только об открытых полях и методах класса, поскольку поля нашего класса закрыты, то и информации о них нет.

Класс int подробно обсуждался в предыдущей и в этой лекции. Все методы, которые могут вызывать переменные (объекты) класса int, были уже рассмотрены. Тем не менее, из выводимой информации можно узнать и нечто новое, поскольку выдается информация и о статических полях и методах класса.




Поделиться с друзьями:


Дата добавления: 2014-12-25; Просмотров: 588; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.013 сек.