1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 102
Текст из файла (страница 102)
Оно гласит "Assignment made to same variable; didyou mean to assign something else?" ("Выполняется присваивание той же переменной. Не намеревались ли вы выполнить иное присваивание?"). Можно ливыразиться понятнее?Можно попробовать изменить строку на t h i s . n I D = n I D и снова пошагово выполнить программу.
(С некоторыми ограничениями вы можете также просто изменитьисходный текст и продолжить отладку — эта возможность Visual Studio называется Editand Continue — поищите информацию о ней в справочной системе.)В этот раз следует аккуратно проверить объекты si и s2 в окне Locals после их конструирования, но, кажется, все выглядит хорошо. Однако пошаговое выполнение очередного вызова W r i t e L i n e () дает странный вывод на экран:Эта программаStudent 1 =неработает!Что могло случиться на этот раз? Вероятно, T o S t r i n g () ничего не возвращает. Необходимо начать сначала, так что пока что я прекращаю отладку.Главное - вовремя остановитьсяВероятно, вы уже замаялись в очередной раз пошагово проходить программу. Пошаговый проход большой программы представляется вообще сплошным кошмаром.Отладчик Visual Studio позволяет указать, что вы хотите остановить программу в ееконкретной точке.
Это достигается путем создания так называемой точки останова(breakpoint).Для этого нужно щелкнуть мышью на области слева от интересующей командыW r i t e L i n e ( ) , где предполагается приостановить выполнение программы. Возле строки появляется маленький красный кружок, а сама строка подцвечивается красным цветом, что свидетельствует о наличии точки останова. Теперь можно указать программе,что она может начинать выполнение, посредством команды меню D e b u g ^ S t a r t или клавиши <F5>. В результате ни о чем не нужно беспокоиться, зная, что программа остановится, дойдя до указанной строки.Как и должно быть, программа начинает работу и останавливается в заданной точке,как показано на рис. 21.17.
Обратите внимание на желтую стрелку, появляющуюсяв красном кружке, и на подцвечивание инструкции W r i t e L i n e () желтым цветом.Далее следует вновь три раза нажать <F10>, чтобы добраться до инструкции W r i t e L i n e ( ) , которая выводит информацию о студенте 1, и затем — <F11>, чтобы попасть вметод T o S t r i n g ( ) .Глава 21. Использование интерфейса Visual Studio517Рис.
21.17. Желтая стречка указывает, где остановилась программа из-заналичия точки остановаЯ не должен был передавать результат вызова S t r i n g . F o r m a t О оператору r e t u r n , как показано в следующей строке:returnString.Format("{0}({l})",sStudentName,nID);Вместо этого следовало бы переписать T o S t r i n g () с использованием временнойпеременной s:publicoverridestringToString(){string s =return s;String.Format("{0}({l})",sStudentName,nID);}Присваивание возвращаемого значения промежуточной переменной дает возможность просмотреть его в отладчике.
(Помимо этого, нет никаких иных причин поступать таким образом, так что после отладки можно удалить эту промежуточную переменную.)Нажмите < F U > для пошагового выполнения строки, вычисляющей значение s —строки, возвращаемой функцией T o S t r i n g ( ) . В окне Locals все выглядит вполне корректно, как видно из рис. 21.18.
(Примечание: \ t , которое вы видите в строке з, представляет собой символ табуляции. Я нажал <ТаЬ> вместо пробела, когда вводил строкуS t r i n g . F o r m a t . На самом деле в этом нет ничего страшного. Вне отладчика выберитекоманду меню E d i t ^ A d v a n c e d ^ S h o w W h i t e S p a c e для того, чтобы вместо пробеловвыводилась точка, а вместо символов табуляции — стрелочка. Отключить этот режимможно аналогичным способом.)Неприятность найденаПроблема должна заключаться в самом вызове W r i t e L i n e ( ) . Следует дважды нажать <F10>, чтобы вернуться к этой строке. Ага! Управляющий элемент { о } , который518Часть VII. Дополнительные главыд о л ж е н в ы в о д и т ь строку, в о з в р а щ а е м у ю T o S t r i n g ( ) , отсутствует. Т о есть в ф у н к ц и юп е р е д а н о з н а ч е н и е д л я него, н о с а м э л е м е н т з а б ы т .
Ч т о б ы и с п р а в и т ь о ш и б к у , д в е команды W r i t e L i n e () надо переписать следующим образом:// d i s p l a y t h e two s t u d e n t sConsole.WriteLine("Student 1Console.WriteLine("Student 2=={o}",{o}",si.ToString());s2.ToString());Рис. 21.18. Возвращаемое значение корректно. Так что же происходит?Вероятно, ошибка произошла в результате перепутывания двух видов функцийWriteLine():// С и с п о л ь з о в а н и е м управляющего э л е м е н т аConsole .WriteLine ("Student 1 = " + s i .
ToString ())';// С и с п о л ь з о в а н и е м управляющего э л е м е н т аC o n s o l e . W r i t e L i n e ( " S t u d e n t 1 = {о}",si.ToString());Подсказка о данныхСейчас самое время рассказать об одном очень ценном нововведении в отладчикеVisual Studio 2005: подсказке о данных (DataTip). Такая подсказка представляет собойнебольшой прямоугольник, который появляется, когда вы останавливаете курсор над переменной во время останова в отладчике. После того как я дважды нажал <F10> для возврата в функцию M a i n ( ) , я помещаю курсор над переменной s i внутри вызова W r i t e L i n e ( ) (без щелчка) и вижу появившееся окошко с s i и его значением T o S t r i n g ( ) :{ S t u d e n t ID ( 1 ) }.
Я игнорирую маленький квадратик, происхождение которого из-завведенного символа табуляции я пояснял ранее. Информацию о подсказке о данныхможно получить из раздела "DataTip" справочной системы.Подсказки работают только когда переменная находится "в контексте", т.е. либо в изучаемой в настоящий момент функции, либо является членом-даннымитекущего класса, и вы уже выполнили строку, в которой инициализируется этапеременная.Глава 21. Использование интерфейса Visual Studio519Теперь о существенном усовершенствовании подсказок, видном из рис. 21.19.
Поместите курсор мыши над знаком + в окошке s l . При этом откроется детальная информация об объектеs l . Если вы переместите курсор на значок +перед S t a t i cm e m b e r s , а п о т о м — перед a l l S t u d e n t s , а з а т е м — перед [ 0 ] — нулевым членомA r r a y L i s t объекта a l l S t u d e n t s — то вы опять увидите sl — в этот раз уже внутриA r r a y L i s t объекта a l l S t u d e n t s .Рис. 21.19. Подсказка о данных— отличное средство, чтобы быстро разобраться с содержимым объекта в отладчикеПодсказки позволяют погружаться все глубже и глубже в сложные объекты. (Ранеедля получения этой информации необходимо было открывать окно W a t c h или QuickW a t c h для данной переменной, либо использовать окно Locals.)Снова щелкните на красном кружке (см.
рис. 21.17) для того, чтобы удалитьточку останова. (Вы можете также воспользоваться командой меню D e b u g sDelete All Breakpoints.) В меню Debug имеется еще одна команда D e b u g sW i n d o w s 1 ^ B r e a k p o i n t s , которая предоставляет доступ ко всем возможностямточек останова.Стек вызововДалее я ставлю точку останова н а вызове O u t p u t A l l S t u d e n t s ( ) , следующем непосредственно за двумя только что исправленными вызовами W r i t e L i n e ( ) . Я нажимаю <F5> для выполнения программы до этой точки и смотрю, что выведено в окнеC o n s o l e . Все выглядит как надо.Затем я еще раз нажимаю <F10>, чтобы пропустить вызов O u t p u t A l l S t u d e n t s ( ) .И вот тут-то это и происходит.Появляется сообщение об ошибке наподобие показанного на рис.
21.20. Оно привязано к строке с циклом f o r , а именно к условию цикла, в котором вызывается свойствоC u r r e n t итератора (об итераторах см. главу 20, "Работа с коллекциями"). Сообщениео б ошибке гласит: E n u m e r a t i o n h a s n o t s t a r t e d . C a l l M o v e N e x t (Перечислениене начато. Вызовите M o v e N e x t ) —- все, что следует знать о происшедшем.520Часть VII. Дополнительные главыРис.
21.20. Visual Studio говорит о том, что забыт начальный вызов MoveNext ()Я закрываю окно сообщения об ошибке и, чтобы получить немного дополнительнойинформации, командой меню D e b u g O W i n d o w s ^ C a l l Stack открываю окно стека вызовов Call Stack, показанное на рис. 21.21 (здесь оно раскрыто для того, чтобы было лучше видно представленную им информацию).Puc; 21.21. Окно Call Stack полезно при поиске источника фатальной ошибкии для определения вашего местоположенияГлава 21. Использование интерфейса Visual Studio521Здесь содержится вся информация, которую может предоставить отладчик — и, какправило, ее достаточно много. Желтая стрелка и подцветка в окне редактора указываютна выражение, вызвавшее проблемы. Окно Call Stack описывает, как именно мы попалив точку генерации исключения: функция Main() вызвала OutputAllStudents ()в строке 64.
Информация о номерах строк в полосе состояния говорит, что строка 64 —это заголовок цикла for, который уже был указан окном с сообщением об ошибке.Вы можете вывести номера строк в окне редактирования — для этого воспользуйтесь командой меню Tools^OptionsOEditor^C* и выберите Line Numbers.В данном случае стек вызовов не оказывает особой помощи, так как включает толькодве функции: Main() и OutputAllStudents ( ) . Но в больших программах можетбыть очень сложно обнаружить, как именно вы попали в эти жернова — и стек вызововокажет вам в таком случае неоценимую помощь.Дополнительную информацию можно получить, щелкнув на ссылке View Detail в окне сообщенияоб исключении и воспользовавшись полемInnerException(единственное со знаком + перед ним). Поместите курсор над StackTrace и получитедополнительную информацию. В верхней строке содержится фраза get_Current.
Вотгде настоящая неприятность — в вызове свойства Current итератора.Беглый взгляд на документацию по свойству IEnumerator. Current проясняет,что не вызван метод MoveNext () перед попыткой получения первого элемента. Теперь,когда стало понятно, в чем дело, следует остановить отладчик щелчком на кнопке StopDebugging в полосе инструментов Debug и вернуться в режим редактирования. (Кнопкапанели инструментов — это ярлык команды меню D e b u g ^ S t o p Debugging; аналогичного эффекта можно добиться и с помощью клавиш <Shift+F5>.)В этот момент у меня накапливается несколько симптомов, указывающих насвойство Current итератора.
Исключение подцвечивает условие цикла for,где вызывается Current, StackTrace в InnerException также упоминает закулисное имя Current — get_Current. (Свойства в действительностиреализуются за сценой с использованием методов с префиксами get_ илиset_.) Итак, вопрос — что же такого могло начудить свойство Current? Ответ — я не вызвал сначала MoveNext ( ) , так что свойство Current не получило начальное значение — первый элемент данных в ArrayList.Дальнейшее исследование показывает, что весь цикл for — одна большая ошибка.Итератор завершает работу, когда MoveNext () возвращает false, а не когда Current получает значение null. Обновленный цикл (теперь — более безопасный while)выглядит следующим образом:publicstatic void OutputAllStudents(){IEnumerator iter = allStudents.GetEnumerator();while(iter.MoveNext()) // 'while 1 , а не 'for'{Student s = (Student)iter.Current;Console.WriteLine("Student = {o}", s.ToString());}}522ЧастьVII.