Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 12
Текст из файла (страница 12)
Переменные ссыпочного типа инициализируются либо с помощью операции пен для создания объекта в управляемой куче, либо присваиванием другой переменной совместимого типа. В следующем фрагменте кода две переменных указывиот на одтн и тот же объект; оЬ)есг о1 = пен оЬ)еск О оЬ)есг о2 = о1; Подобно исполняющей системе дача, среда СЬК управляет всеми ссылками на объекты в куче. В С++ необходимо явно удалять объекты, расположенные в куче, причем в некоторый тщательно выбранный момент.
Но в управляемой среде СЬК за вас это делает ОС. Это избавляет от необходимости заботиться об удалении объектов из памяти и минимизирует утечки памяти. В любой момент времени ОС может определить количество ссылок на определенный объект в куче. Если он выясняется, что ссылок нет, значит, можно начать процесс уничтожения объекта в куче. (В главе 13 обсуждается сложность этого процесса и факторы, которые на нее влияют.) Предыдущий фрагмент кода включает две ссылки на один и тот же объект.
Первая из них, о1, инициализируется созданием нового экземпляра оЬ) есС, а вторая — о2 — инициализируется присваиванием первой, о1. ОС не удалит этот объект из кучи до тех пор, пока обе ссылки не выйдут из их области видимости. Если бы метод возвращал копию ссылки тому, кто его вызывает. то СС продолжал бы отслеживать ссылку на данный объект, даже несмотря на то, что создавший его метод уже завершился.
На заметку! Примечание для тех, кто пришел иэ мира С++; фундаментальный способ трактовки объектов С++ в мире С№ "вывернут наизнанку". В С++ объекты размещаются в стеке, если только они не создаются явно с помощью операции пеи, которая возвращает указатель на объект в "родной" куче. В С№ создавать обьекты ссылочных типов в стеке нельзя, Они могут существовать только в куче. Так что это похоже на то, как если бы вы писали код на С++, в котором создавали каждый объект в куче, не заботясь о последующем его явном удалении для очистки памяти.
Инициализация переменных по умолчанию По умолчанию компилятор С№ производит то, что называется безопасным кодом (зэ(е себе). Один из аспектов безопасности состоит в том, что программа не использует неинициализированную память. Компилятор требует, чтобы каждой переменной было присвоено значение прежде, чем можно будет работать с ней, поэтому полезно знать, как инициализируются переменные разных типов. Значением по умолчанию для ссылок на объекты является по11. В точке объявления можно дополнительно присваивать ссылки, полученные в результате вызова операции 46 Глава 3 пеи; в противном случае они остаются установленными в пц11. Когда создается объект, исполняющая система инициализирует его внутренние поля.
Поля, являющиеся ссылками на объекты, разумеется, инициализируются значением пц11. Поля, относящиеся к типам значений, инициализируются установкой всех битов значения в ноль. По сути, можно представлять, что все, что делает исполняющая система — это установка лежащего в основе хранилища в О. Для ссылок на объекты это соответствует пс11-ссылке, а для типов значений — нулевому значению (или га1зе для булевского типа). Для типов значений, которые объявляются в стеке, компилятор не выполняет автоматическую инициализацию нулями. Однако перед использованием значения память должна быть инициализирована. На ааметку1 Перечисления на самом деле являются типами значений, поэтому несмотря на то, что 0 может быть неявно преобразован в любой тип перечисления, принципы хорошего проектирования требуют всегда объявлять член перечисления, равный нулю, даже если именем члена является 1пта11г)Ча1це, Мопс или что-то другое, лишенное смысла.
Если перечисление объявлено как поле класса, экземпляры этого класса будут иметь это поле установленным в ноль при инициализации по умолчанию. Объявление члена, равного нулю, позволяет пользователям перечисления легко справиться с такой ситуацией и способствует созданию более читабельного кода. Неявно типизированные локальные переменные Поскольку С№ — строго типизированный язык, каждая объявленная в коде переменная должна иметь определенный тнп, ассоциированный с ней. В версии С№ 4.0 это правило было несколько ослаблено появлением типа с)упав1с,который рассматривается в главе 17. Когда среда СЬВ сохраняет содержимое переменной в определенном месте памяти, она также ассоциирует тип с этим местоположением.
Но иногда при написании кода для строго типизированных языков объем ввода, необходимый для объявления таких переменных, может оказаться довольно большим, особенно если речь идет об обобщенном типе. Для сложных типов, таких как переменные запросов. созданные с применением язьгка интегрированных запросов (Ы)))Я), о которых пойдет речь в главе 16, имена типов могут быть очень громоздкими. В этом случае обратитесь к неявно типизированным переменным.
Объявляя локальную переменную с использованием нового ключевого слова тат, вы в действительности просите компьютер зарезервировать локальный участок памяти и затем каким-то образом присоединить к нему выведенный тип. Во время компиляции у компилятора достаточно доступной информации в точке инициализации переменной.
чтобы вывести действительный тип этой переменной, без необходимости с вашей стороны указывать тип явно. Давайте посмотрим, как это выглядит. Ниже приведен пример объявления неявно типизированной переменной: сзапс Бузгекп сзгпо Яузтев.Со11есстопз.оепегасг рсЬ11с с1ззз Ептгугойпт ( зтаттс тока Ма1п() ( так иуьгзс = пен 11зт<1пс> О г туъйзС.АПП( 1 ); вуъазС.АПП( 2 ); вуъазС.АПП( 3 ); гогеасп( так 1 ап вуъ1зк ) ( Сопзо1е.нггте11пе( 1 ); ) ) Обзор синтаксиса С№ 47 Первое, что сразу бросается в глаза — это выделенные полужирным новые ключевые слова чаг. В первом случае объявляется переменная по имени шуььзг, тип которой будет установлен компилятором на основе типа присваиваемого ей выражения. Важно отметить, что объявление неявно типизированной переменной должно включать инициализатор.
При попытке написать в коде нечто, подобное тому, что показано ниже, компилятор выдаст ошибку СБ0810 с сообщением "!шр)гсШу гуреб 1оса1з пшэГ Ье 1пГПайзеб" (Неявно типизированные локальные переменные должны быть инициализированы): чаг пенуа1ие; // выдаст ошибку СБ0818 Аналогично, если попытаться объявить поле-член класса как неявно типизированную переменную, даже если она будет иметь инициализатор, возникнет ошибка компиляции С80825, сообщающая: "ТЬе сопгехтца) )геуаюп) 'чаг' гпау оп!у арреагэ чЛГ)пп а 1оса1 чаг1аЫе бес1агацоп" (Контекстуальное ключевое слово ча г может применяться только в объявлении локальной переменной).
И, наконец, в одной строке можно объявлять сразу нескольких переменных, отделяя каждую из них запятой, как показано в следующем примере: гпс а = 2, Ь = 1; 1пгх, у=41 Однако поступать так с неявно типизированными переменными нельзя. Компилятор выдаст ошибку С80819 с сообщением Ьпрйс1Г)у гуреб 1оса1э саппог Ьаче пппб)Пр1е бес1агаПопз" [Неявно типизированные локальные переменные не допускают множественного объявления). Сложность добавления новых ключевых слов к языку Добавление в компилятор новых средств, подобных неявно типизированным переменным — не такая тривиальная задача, как может показаться на первый взгляд. Это связано с тем, что всякий раз, когда в язык вводится новое слово, должна быть учтена возможность нарушения работы существующего кода и соблюдена обратная совместимость.
Например, представьте, что случится, если есть громадная база кода, написанного на С№ 1.0 ипи С№ 2.0, в которой присутствует некий тип, скажем, класс, по имени чаг. Теперь вы собираетесь перенести это все на С№ 3.0 ипи последующую версию языка и компилируете приложение с применением нового компилятора. Понятно, что наверняка существуют некоторые объявления переменных, создающие экземпляры класса чаг. Что в этом случае должен делать компилятор? Ответить на этот вопрос очень непросто.
При проведении тестов для описанной выше ситуации компилятор не делал ничего. Но должно пи быть так? Компилятор мог бы выдать предупреждение, сообщающее нечто вроде "объявленный тип имеет имя, совпадающее со встроенным ключевым словом чаг", Как вскоре будет показано, на самом деле это вовсе нв обязательно. Фактически даже лучше, чтобы компилятор не выдавал предупреждение подобного рода.
Опытные разработчики используют опцию компилятора /иддндякккокБ+, чтобы остановить компиляцию в случае появления предупреждений в коде, Если компилятор выдал предупреждение, то после перехода на С№ 3.0 ипи последующую версию языка приложение компилироваться не будет, а М!сгозой должна будет подвергнута критике за то, что игнорирует принципы обратной совместимости.
Итак, суть состоит в том, что если имеется тип, определенный с именем чаг, то просто нет возможности испольэовать явно типизированную переменную в любом коде С№, где пространство имен этого типа импортировано в локальный контекст. Если это сделать, то обычно возникает ошибка компиляции С80029, которая говорит о том, что тип, который назначается неявно типизированной переменной, не может быть неявно преобразован в пользовательский тип чаг. Описанное поведение продемонстрировано в следующем примере кода: 48 Глава 3 ия1по Буясекп иязпд Буягеп.СЬ11ес11опя.6епег1сг РиЬ1зс с1аяя чаг ( ) РиЬ11с с1аяя Бпггугозпк ( ясас1с чо1б Маьп() ( чаг пуьаяс = пен ьзяс<1пс>()! г/ не компилируется! Овнбка СБ0029 ) ! Разработчики компиляторов обычно относятся к подобным проблемам чрезвычайно серьезно, и иногда даже отказываются от ввода нового ключевого слова, если есть вероятность разрушения существующего кода.











