1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 33
Текст из файла (страница 33)
s N a m e = sName;}}publicclassProgram{publicstaticvoidMain(string[]args){Studentstudent= new S t u d e n t ( ) ;// П р и с в а и в а е м имя н е п о с р е д с т в е н н ы м обращением к// объектуConsole .WriteLine("Сначала:");student.sName = "Madeleine";Student.OutputName(student);// Теперьфункция// принадлежит классу// Student// И з м е н я е м имя с помощью ф у н к ц и иConsole.WriteLine("Послеизменения:");Student.SetName(student, "Willa");Student.OutputName(student);// Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яConsole.WriteLine("Нажмите <Enter> для " +"завершения программы...");Console.Read();}}По сравнению с программой P a s s O b j e c t данная программа имеет только одно важщ|изменение: функции O u t p u t N a m e () и S e t N a m e () перенесены в класс S t u d e n t .Из-за этого изменения функция M a i n () вынуждена обращаться к означенным фущ|циям с указанием класса S t u d e n t .
Эти функции теперь являются членами класса Stu-|d e n t , а н е P r o g r a m , которому принадлежит функция M a i n ( )Это маленький, но достаточно важный шаг. Размещение функции O u t p u t N a m e !в классе приводит к повышению степени повторного использования: внешние функцкоторым требуется выполнить вывод объекта на экран, могут найти функцию OutputN a m e () вместе с другими функциями в классе, следовательно, писать такие функдля каждой программы, применяющей класс S t u d e n t , не требуется.Указанное решение лучше и с философской точки зрения.
Класс P r o g r a m не долябеспокоиться о том, как инициализировать имя объекта S t u d e n t , или о том, как выпо:нить вывод его имени на экран. Всю эту информацию должен содержать сам класс Stud e n t . Объекты отвечают сами за себя.В действительности функция M a i n () не должна инициализировать объемименем Madeleine непосредственно — в этом случае также следует использ<вать функцию S e t N a m e ( ) .180Часть III. Объектно-основанное программированаВнутри самого класса S t u d e n t одна функция-член может вызывать другую без явного указания имени класса.
Функция S e t N a m e () может вызвать функцию O u t p u t feme ( ) , не указывая имени класса. Если имя класса не указано, С# считает, что вызванафункция из того же класса.Определение методаОбращение к членам данных объекта — экземпляра класса — выполняется посредством указания объекта, а не класса:Student s t u d e n tstudent.sName= new S t u d e n t () ;= "Madeleine";////Создание экземпляраОбращение к членуStudentC# позволяет вызывать нестатические функции-члены аналогично:student. S e t N a m e ( " M a d e l e i n e " ) ;Следующий пример демонстрирует это://InvokeMethodusing S y s t e m ;namespace-вызовфункции-членасуказаниемобъектаInvokeMethod{class S t u d e n t{// Информацияpublicstringpublicstring// SetNamepublic-voidоб имени с т у д е н т аsFirstName;sLastName;сохранениеинформацииSetName(stringsFName,обимениstringsLName){sFirstNamesLastName==sFName;sLName;}// ToNameString - п р е о б р а з у е т// строку д л я выводаpublics t r i n g ToNameString()объектклассаStude{stringreturn s;s=sFirstName+""+sLastName;}}publicclassProgram{publics t a t i cvoidMain(){Studentstudent=newStudent();student.SetName("Stephen",Глава 8.
Методы класса"Davis");181Console.WriteLine("Имя студента"+ student.ToNameString());// Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яConsole.WriteLine("Нажмите <Enter> для " +" з а в е р ш е н и я п р о г р а м м ы . . . ") ;Console.Read();}}}Вывод данной программы состоит из одной строки:ИмястудентаStephenDavisЭта программа очень похожа на программу P a s s O b j e c t T o M e m b e r F u n c t i o n . B введенной версии используются нестатические функции для работы с именем и фамилией,Программа начинает с создания нового объекта s t u d e n t класса S t u d e n t , послего вызывает функцию S e t N a m e ( ) , которая сохраняет строки " S t e p h e n " и "Daviв членах-данных s F i r s t N a m e и s L a s t N a m e . И наконец, программа вызывает сцию-член T o N a m e S t r i n g ( ) , возвращающую имя студента, составленное из двух аПо историческим причинам, никак не связанным с С#, нестатические функцчлены называются методами.
В книге будет использоваться термин метод]нестатических функций-членов, и термин функция — для функций всех протипов.Некоторыепрограммистыприменяюттерминыметодэкземп(нестатический) и метод класса (статический).Вернемся вновь к методу S e t N a m e ( ) , предназначенному для изменения значенийлей объекта класса S t u d e n t . Какой именно объект модифицирует метод SetName!Рассмотрим, как работает следующий пример:S t u d e n t c h r i s t a = new S t u d e n t ( ) ; // Создаем двух совершенноStudent sarah= new S t u d e n t ( ) ; // разных с т у д е н т о вchrista.SetName("Christa","Smith");sarah.SetName("Sarah","Jones");Первый вызов S e t N a m e () изменяет поля объекта c h r i s t a , а в т о р о й — объектsarah.еПрограммисты на С# говорят, что метод работает с текущим объектом.
В первом вызове текущим объектом является c h r i s t a , в о втором — s a r a h .Полное имя методаИмеется тонкая, но важная проблема, связанная с описанием имен методов. Рассмотрим следующий фрагмент исходного текста:publicclassPerson{publicvoidAddress(){Console.WriteLine("Hi");182Часть III. Объектно-основанное программномpublic(classLetterstring sAddress;// Сохранение а д р е с аpublicvoidAddress(stringsNewAddress)}sAddress = sNewAddress;}}Любое обсуждение метода A d d r e s s () после этого становится неоднозначным.
Метод A d d r e s s () класса P e r s o n не имеет ничего общего с методом A d d r e s s () классаL e t t e r . Если кто-то скажет, что в этом месте нужен вызов метода A d d r e s s ( ) , то какой именно метод A d d r e s s () имеется в виду?Проблема не в самих методах, а в описании. Метод A d d r e s s () как независимая самодостаточная сущность просто не с у щ е с т в у е т — существуют методыРегson. A d d r e s s ( ) и L e t t e r . A d d r e s s ( ) . Путем добавления имени класса в началоимени метода явно указывается, какой именно метод имеется в виду.Это описание имени метода очень похоже на описание имени человека. К примеру,в семье меня знают как Стефана (честно говоря, в семье меня зовут несколько иначе, ноэто уже несущественные подробности).
В семье больше нет Стефанов (по крайней мерев моей семье), но вот на работе есть еще два Стефана.Когда я обедаю с коллегами в компании, где нет этих других Стефанов, имя Стефаноднозначно относится ко мне. Но когда все оказываются на рабочих местах, чтобы избежать неоднозначности, следует добавлять к имени и фамилию и обращаться к СтефануДэвису, Стефану Вильямсу или Стефану Лейе.Таким образом, A d d r e s s () можно рассматривать как имя метода, а его класс — какфамилию.Рассмотрим следующий метод S t u d e n t .
S e t N a m e ( ) :class S t u d e n t// Информация об и м е н и с т у д е н т аpublic s t r i n g s F i r s t N a m e ;public s t r i n g s L a s t N a m e ;// SetName - с о х р а н е н и е и н ф о р м а ц и иpublic v o i d S e t N a m e ( s t r i n g sFName,об и м е н иs t r i n g sLName){sFirstName = sFName;sLastName = sLName ;}}public class Program{Шва 8.
Методы классаpublic static void Main()183{S t u d e n t s t u d e n t l = new S t u d e n t ( ) ;studentl.SetName("Joseph","Smith");S t u d e n t s t u d e n t 2 = new S t u d e n t ( ) ;student2.SetName("John","Davis");}}Функция M a i n () использует метод S e t N a m e () для того, чтобы обновить поляомектов s t u d e n t l и s t u d e n t 2 . Но внутри метода S e t N a m e () нет ссылки ни на какяобъект типа S t u d e n t .
Как уже было выяснено, метод работает "с текущим объектовНо как он знает, какой именно объект — текущий?Ответ прост. Текущий объект передается при вызове метода как неявный аргумент-Iнапример, вызовstudentl.SetName("Joseph","Smith");эквивалентен следующему:Student.SetName(studentl,"Joseph","Smith");// Это - э к в и в а л е н т н ы й вызов (однако э т о т вызов// корректно скомпилирован)небудетЯ не хочу сказать, что вы можете вызвать метод S e t N a m e () двумя различными спсобами, я просто подчеркиваю, что эти два вызова семантически эквивалентны. Объеявляющийся текущим — скрытый первый аргумент — передается в функцию так же,и другие аргументы.
Оставьте эту задачу компилятору.А что можно сказать о вызове одного метода из другого? Этот вопрос иллюстрируся следующим фрагментом исходного текста:publicclassStudent{p u b l i c s t r i n g sFirstName,publics t r i n g sLastName;public void SetName(string sFirstName,s t r i n g sLastName)SetFirstName(sFirstName);SetLastName(sLastName);publicvoidSetFirstName(stringsFirstNamepublicvoidsLastName=sName;SetLastName(string=sName)sName)sName;}В вызове S e t F i r s t N a m e () не видно никаких объектов. Дело в том, что при вызок|одного метода объекта из другого в качестве неявного текущего объекта передается тиже объект, что и для вызывающего метода.
Обращение к любому члену в методе объемрассматривается как обращение к текущему объекту, так что метод знает сам, какомименно объекту он принадлежит.184Часть III. Объектно-основанное программирование]Ключевое слово thisВ отличие от других аргументов, текущий объект в список аргументов функции непопадает, а значит, программист не назначает ему никакого имени. Однако С# не оставляет этот объект безымянным и присваивает ему не слишком впечатляющее имя t h i s ,которое может пригодиться в ситуациях, когда вам нужно непосредственно обратиться ктекущему объекту.t h i s — ключевое слово С#, так что оно не может использоваться в программени для какой иной цели, кроме описываемой.Итак, можно переписать рассматривавшийся выше пример следующим образом:publicclassStudent(publicpublicpublicstring sFirstName;string sLastName;void SetName(stringsFirstName,stringsLastName){// Явная с с ы л к а на " т е к у щ и й о б ъ е к т "// ключевого слова t h i sthis.SetFirstName (sFirstName) ;сприменениемthis.SetLastName (sLastName) ;publicvoidSetFirstName(stringthis.