Лекция-03_Пр-е.ppt
- Количество слайдов: 25
Системные типы данных и их представление в C# (продолжение) Лекция 18. 09. 12 г. 1
Область видимости переменных Область видимости, или контекст, переменной — это часть кода, в пределах которого доступна данная переменная. Такая область определяется следующими правилами: Ø Поле, или переменная-член класса, находится в области видимости до тех пор, пока в этой области находится содержащий поле класс (это так же, как и в C++, Java и VB) Ø Локальная переменная находится в области видимости до тех пор, пока закрывающая фигурная скобка не укажет конец блока операторов или метода, в котором она объявлена Ø Локальная переменная, объявленная в операторах for, while или подобных им, видима в пределах тела цикла Ø Локальные переменные с одним и тем же именем не могут быть объявлены дважды в одном и том же контексте, поэтому следующий фрагмент кода неверен: int х = 20; // какой-то код int х = 30; Лекция 18. 09. 12 г. 2
Область видимости переменных В следующем примере переменная i объявляется дважды в пределах одного и того же метода. Это можно делать, поскольку i объявляется в двух разных циклах, и поэтому каждая из переменных i локальна в пределах своего цикла. public static int Main() { for (int i = 0; i < 10; i++) { Console. Write. Line(i); } // i покидает область видимости // Мы можем объявить переменную i снова, потому что // не существует второй переменной i в данной области видимости for (int i = 9; i >= 0; i--) { Console. Write. Line(i); } // i покидает область видимости return 0; } Лекция 18. 09. 12 г. 3
Область видимости переменных Внесем небольшое изменение в предыдущий код: public static int Main() { int i = 10; for (int i = 0; i < 10; i++) { Console. Write. Line(i); } // i покидает область видимости // Мы можем объявить переменную i снова, потому что // не существует второй переменной i в данной области видимости for (int i = 9; i >= 0; i--) { Console. Write. Line(i); } // i покидает область видимости return 0; } При компиляции получим ошибку, т. к. переменная i, которая определена перед началом цикла for, внутри цикла все еще находится в области видимости и не может из нее выйти до завершения метода Main(). Хотя другая переменная i (недопустимая) объявлена в контексте цикла, этот контекст вложен в контекст метода Main(). Компилятор не может различить эти две переменных, поэтому не допустит объявления второй из них. Это отличается от правил C++, где допустимо сокрытие переменных. Лекция 18. 09. 12 г. 4
Область видимости переменных Еще один эксперимент: using System; namespace Scope. Test { class Scope. Test 2 { static int i = 20; public static int Main() { for (int i = 0; i < 10; i++) { Console. Write. Line(i); } // i покидает область видимости for (int i = 9; i >= 0; i--) { Console. Write. Line(i); } // i покидает область видимости Console. Write. Line(Scope. Test 2. i); Console. Read. Line(); return 0; } } } В этом коде ошибок нет, т. к. статическая переменная i имеет контекст класса Scope. Test 2 и не конфликтует с локальными переменными i. Лекция 18. 09. 12 г. 5
Константы Константа — это идентификатор, значение которого никогда не меняется. Компилятор должен получить значение идентификатора константы во время компиляции. Затем он сохраняет это значение в метаданных модуля. Константы можно определять только для элементарных типов. Т. к. значения констант никогда не меняются, константы считаются частью типа, т. е. они являются статическими, а не экземплярными членами. Встретив в исходном тексте идентификатор константы, компилятор извлекает из метаданных модуля, в котором она определена, значение константы и подставляет его в генерируемый им IL-код вместо идентификатора константы; поэтому в период выполнения память для констант не выделяется. Кроме того, нельзя получать адрес константы и передавать ее по ссылке. Отсюда также следует, что изменять значения константы в разных версиях модуля нельзя (точнее, бесполезно, т. к. это значение «извлекается» только один раз, при компиляции кода, использующего эту константу). const int a = 100; // Это значение не может быть изменено Лекция 18. 09. 12 г. 6
Иерархия классов типов данных В. Net даже элементарные типы данных организованы в виде иерархии классов. Типы, которые находятся в самом верху иерархии, обеспечивают некоторое поведение по умолчанию, которое передается унаследованным типам. Схематично отношения между базовыми системными типами показаны на рисунке. Лекция 18. 09. 12 г. 7
Иерархия классов типов данных Boolean Object Byte Type String Array Exception Delegate Value. Type Char Любой тип, который наследуется от Value. Type, представляет собой структуру или перечисление, а не класс Decimal Double Int 16 Int 32 Int 64 Multicast. Delegate SByte Uint 16 UInt 32 UInt 64 Void Date. Time Guid Time. Span Single Перечисления или структуры Лекция 18. 09. 12 г. 8
Иерархия классов типов данных q Каждый из указанных на рисунке типов в конечном итоге наследуется от класса System. Object, в котором определен ряд методов: To. String(), Equals(), Get. Hash. Code() и пр. , общих для всех. Net типов, поставляемых в библиотеках базовых классов. q Многие числовые типы данных наследуются от класса System. Value. Type. Потомки Value. Type автоматически размещаются в стеке и потому обладают предсказуемым временем жизни и являются эффективными. q Типы, у которых в цепочке наследования не присутствует класс System. Value. Type, в стеке не размещаются, но зато размещаются в куче с автоматической сборкой мусора (например, типы System. Type, System. String, System. Array, System. Exception и System. Delegate). Лекция 18. 09. 12 г. 9
Иерархия классов типов данных Так как любое ключевое слово в С# (например, int) представляет собой всего лишь сокращенное обозначение соответствующего системного типа (в данном случае— System. Int 32), приведенный ниже синтаксис вполне допустим: static void Object. Functionality() { Console. Write. Line("=> System. Object Functionality: "); // C# int есть сокращение System. Int 32, // который наследует следующие члены от System. Object: Console. Write. Line("12. Get. Hash. Code() = {0}", 12. Get. Hash. Code()); Console. Write. Line("12. Equals(23) = {0}", 12. Equals(23)); Console. Write. Line("12. To. String() = {0}", 12. To. String()); Console. Write. Line("12. Get. Type() = {0}", 12. Get. Type()); Console. Write. Line(); } Лекция 18. 09. 12 г. 10
Предопределенные ссылочные типы q Тип object — это первичный родительский тип, от которого наследуются все внутренние и пользовательские типы. Это — ключевое свойство С#, которое отличает его как от Visual Basic 6. 0, так и от C++, хотя здесь его поведение очень похоже на Java. Все типы неявно унаследованы от класса System. Object. q Тип string — ссылочный тип. Объекты типа string размещаются в куче, а не в стеке, и присвоении одной строки другой, получаем две ссылки на одну и ту же строку в памяти. Однако поведение string несколько отличается от поведения ссылочных типов. Например, стоит внести изменение в одну из этих строк, как тут же будет создан совершенно новый объект string, а старый останется неизменным. Лекция 18. 09. 12 г. 11
Члены числовых типов данных q Числовые типы в. Net поддерживают свойства Max. Value и Min. Value, предоставляющие информацию о диапазоне значений, которые могут храниться в данном типе. q Тип System. Double позволяет получать значения эпсилона и бесконечности (которые могут представлять интерес при решения вычислительных задач). static void Data. Type. Functionality(){ Console. Write. Line("=> Data type Functionality: "); // максимальное значение типа int Console. Write. Line("Max of int: {0}", int. Max. Value); // минимальное значение типа int Console. Write. Line("Min of int: {0}", int. Min. Value); // максимальное значение типа double Console. Write. Line("Max of double: {0}", double. Max. Value); // минимальное значение типа double Console. Write. Line("Min of double: {0}", double. Min. Value); // эпсилон значение типа double Console. Write. Line("double. Epsilon: {0}", double. Epsilon); // значение + бесконечности типа double Console. Write. Line("double. Positive. Infinity: {0}", double. Positive. Infinity); // значение - бесконечности типа double Console. Write. Line("double. Negative. Infinity: {0}", double. Negative. Infinity); Console. Write. Line(); } Лекция 18. 09. 12 г. 12
Члены типа System. Boolean Единственными значениями, которые могут присваиваться типу bool в С#, являются true и false. System. Boolean поддерживает свойства True. String/False. String (которые делают строку, соответственно, "истинной" или "ложной"). Приведем пример использования данных свойств. Добавим в предыдущий код следующие операторы: Console. Write. Line("bool. False. String: {0}", bool. False. String); Console. Write. Line("bool. True. String: {0}", bool. True. String); Лекция 18. 09. 12 г. 13
Члены типа System. Char Помимо возможности хранить один элемент символьных данных, тип System. Char обладает и другими полезными функциональными возможностями. С помощью статических методов типа System. Char можно определять, является ли данный символ цифрой, буквой, знаком пунктуации или чем-то еще. Например, рассмотрим следующий метод: static void Char. Functionality() { Console. Write. Line("=> char type Functionality: "); char my. Char = 'a'; Console. Write. Line("char. Is. Digit('a'): {0}", char. Is. Digit(my. Char)); Console. Write. Line("char. Is. Letter('a'): {0}", char. Is. Letter(my. Char)); Console. Write. Line("char. Is. White. Space('Hello There', 5): {0}", char. Is. White. Space("Hello There", 5)); Console. Write. Line("char. Is. White. Space('Hello There', 6): {0}", char. Is. White. Space("Hello There", 6)); Console. Write. Line("char. Is. Punctuation('? '): {0}", char. Is. Punctuation('? ')); Console. Write. Line(); } Лекция 18. 09. 12 г. 14
Извлечение значений из строковых данных Типы данных. Net позволяют извлекать значение соответствующего типа, имея текстовый эквивалент. В процессе извлечения выполняется синтаксический анализ. Приведем пример. static void Parse. From. Strings() { Console. Write. Line("=> Data type parsing: "); bool b = bool. Parse("True"); //из string в тип bool Console. Write. Line("Value of b: {0}", b); double d = double. Parse("99. 884"); //из string в тип double Console. Write. Line("Value of d: {0}", d); int i = int. Parse("8"); //из string в тип int Console. Write. Line("Value of i: {0}", i); char c = Char. Parse("w"); //из string в тип char Console. Write. Line("Value of c: {0}", c); Console. Write. Line(); } Лекция 18. 09. 12 г. 15
Члены типа System. String В таблице перечислены некоторые наиболее интересные члены этого типа. Член Описание Length Это свойство возвращает информацию о длине текущей строки. Compare() Этот метод позволяет сравнивать две строки. Contains() Этот метод позволяет определять, содержит ли строка определенную подстроку. Equals() Этот метод позволяет проверять, содержат ли два строковых объекта идентичные символьные данные. Format() Этот метод позволяет форматировать строку с помощью других элементарных типов данных (например, числовых данных или других строк). Insert() Этот метод позволяет вставлять строку внутри определенной строки. Pad. Left(), Pad. Right() Эти методы применяются для дополнения строки символами, соответственно, справа и слева. Remove(), Replace() Эти методы позволяют получать копию строки с соответствующими изменениями (удаленными или замещенными символами). Split() Этот метод возвращает массив String, содержащий присутствующие в данном экземпляре подстроки, которые отделяются друг от друга элементами из указанного массива Char или String Trim() Этот метод позволяет удалять все вхождения указанного ряда символов из начала и конца текущей строки. To. Upper(), To. Lower() Эти методы позволяют создавать копию текущей строки в формате, соответственно, верхнего или нижнего регистра. Лекция 18. 09. 12 г. 16
Конкатенация строк Переменные string могут сцепляться вместе с помощью операции +. Как известно, подобный прием называется конкатенацией строк. Также можно воспользоваться статическим методом Concat типа string. static void String. Concatenation() { Console. Write. Line("=> String concatenation: "); string s 1 = "Programming the "; string s 2 = "Psycho. Drill (PTP)"; Console. Write. Line(string. Concat(s 1, s 2)); Console. Write. Line(s 1 + s 2); Console. Write. Line(); } Лекция 18. 09. 12 г. 17
Управляющие последовательности символов В С# строковые литералы могут содержать различные управляющие последовательности символов (escape characters), уточняющие, каким образом символьные данные должны выводиться в выходном потоке. Каждая управляющая последовательность начинается с символа обратной косой черты, за которым следует конкретный подлежащий интерпретации знак. Приведем управляющие последовательности, которые могут применяться со строковыми литералами Последовательность Описание ’ Позволяет вставлять в строковый литерал символ одинарной кавычки. ” Позволяет вставлять в строковый литерал символ двойной кавычки. \ Позволяет вставлять в строковый литерал символ обратной косой черты. Может оказаться полезной при определении путей к файлам. a Заставляет систему выдать звуковой сигнал. n Позволяет вставлять символ новой строки. r Позволяет вставлять символ возврата каретки. t Позволяет вставлять в строковый литерал символ горизонтальной табуляции. Лекция 18. 09. 12 г. 18
Управляющие последовательности символов За счет добавления к строковому литералу префикса @ можно создавать так называемые дословные строки (verbatim string). Использование дословных строк позволяет отключать обработку управляющих последовательностей в литералах и выводить объекты string в том виде, как они есть (например, задавать путь к директориям). static void Escape. Chars() { Console. Write. Line("=> Escape characters: a"); string str. With. Tabs = "Modelt. Colort. Speedt. Pet Namea"; Console. Write. Line(str. With. Tabs); Console. Write. Line("Everyone loves "Hello World"a"); // В следующей строке сохраняются все символы Console. Write. Line(@"C: My. AppbinDebuga"); // Пробелы сохраняются в дословной строке. string my. Long. String = @"This is a very long string"; Console. Write. Line(my. Long. String); Console. Write. Line(@"Cerebus said ""Darrr! Pret-ty sun-sets"""); // Добавляется 4 пустых строки и звуковой сигнал. Console. Write. Line("All finished. nnna"); Console. Write. Line(); } Лекция 18. 09. 12 г. 19
Типы System. Date. Time и System. Time. Span В типе Date. Time содержатся данные, представляющие конкретную дату (месяц, день, год) и время, которые могут форматироваться разными способами с помощью поставляемых членов. // Этот конструктор принимает в качестве // аргументов сведения о годе, месяце и дне. Date. Time dt = new Date. Time (2012, 9, 23); // Какой это день месяца? Console. Write. Line("The day of {0} is {1}", dt. Date, dt. Day. Of. Week); dt = dt. Add. Days(100); Console. Write. Line("The day of {0} is {1}", dt. Date, dt. Day. Of. Week); dt = dt. Add. Months(2); // Месяц январь. Console. Write. Line("Daylight savings: {0}", dt. Is. Daylight. Saving. Time()); Console. Write. Line("The day of {0} is {1}", dt. Date, dt. Day. Of. Week); Лекция 18. 09. 12 г. 20
Типы System. Date. Time и System. Time. Span Тип Time. Span представляет собой структуру и позволяет легко определять и преобразовывать единицы времени с помощью различных членов этого типа. // Этот конструктор принимает в качестве аргументов // сведения о часах, минутах и секундах. Time. Span ts = new Time. Span (4, 30, 0) ; Console. Write. Line (ts); // Вычитаем 15 минут из текущего значения Time. Span // и отображаем результат. Console. Write. Line(ts. Subtract (new Time. Span (0, 45, 0) ) ) ; Лекция 18. 09. 12 г. 21
Преобразование типов данных Рассмотрим пример: class Program { static void Main(string[] args){ Console. Write. Line("***** Fun with type conversions *****"); // Сложение переменных типа short и отображение результата. short a = 9, b = 10; Console. Write. Line("{0} + {1} = {2}", a, b, Add(a, b)); Console. Read. Line(); } static int Add(int x, int y){ return x + y; } } Метод Add() ожидает два параметра типа int. Вместо этого метод Main() передает ему две переменных short. Хотя здесь наблюдается ситуация полного несоответствия типов, программа компилируется и выполняется без ошибок, возвращая в результате ожидаемое значение 19. Причина, по которой компилятор воспринимает данный код как синтаксически корректный, заключается в том, что здесь нет никакой угрозы потери данных. Поскольку максимальное значение, которое может содержать тип short (32767), вполне вписывается в рамки диапазона типа int (максимальное значение которого составляет 2 147 483 647), компилятор неявно расширяет (widens) каждую переменную типа short до типа int. Лекция 18. 09. 12 г. 22
Преобразование типов данных Термин "расширение" применяется для обозначения неявного "восходящего приведения" (upward cast), которое не приводит к потере данных. Хотя возможность подобного неявного расширения типов в предыдущем примере и дала положительный результат, в других случаях она может стать источником ошибок на этапе компиляции. Например, предположим, что значение, возвращаемое методом Add(), сохранялось в новой локальной переменной short, а не просто напрямую выводилось на консоль. static void Main(string[] args){ Console. Write. Line("***** Fun with type conversions *****"); // Следующий код приведет к генерации ошибки на этапе компиляции! short a = 9, b = 10; short answer = Add(a, b); Console. Write. Line("{0} + {1} = {2}", a, b, answer); Console. Read. Line(); } Ошибка возникла в результате попытки применения операции сужения (narrowing operation), для сохранения большего значения внутри переменной меньшего размера. Лекция 18. 09. 12 г. 23
Преобразование типов данных При желании проинформировать компилятор о необходимости смириться с возможной потерей данных из-за операции сужения, нужно обязательно применить операцию явного приведения типов (explicit casting), которая в С# выглядит как (тип). class Program { static void Main(string[] args) { Console. Write. Line("***** Fun with type conversions *****"); short a = 30000, b = 30000; short answer = (short)Add(a, b); Console. Write. Line("{0} + {1} = {2}", a, b, answer); Narrowing. Attempt(); Console. Read. Line(); } static int Add(int x, int y) { return x + y; } static void Narrowing. Attempt() { byte my. Byte = 0; int my. Int = 260; my. Byte = (byte)my. Int; Console. Write. Line("Value of my. Byte: {0}", my. Byte); } } Лекция 18. 09. 12 г. 24
Обработка аргументов командной строки Рассмотрим метод Main() в приведенном ниже коде. В параметре args будут переданы те параметры, которые мы укажем при запуске нашего консольного приложения из командной строки. static int Main(string[] args) { // Обработка любых входящих аргументов. for (int i = 0; i < args. Length; i++) Console. Write. Line("Arg{0}: {1}", i+1, args[i]); Console. Read. Line(); return 0; } Лекция 18. 09. 12 г. 25


