1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 36
Текст из файла (страница 36)
Подробнее о методах и классах187// Создаем копию объекта ob.public void copy(MyClass ob) {alpha = ob.alpha;beta = ob.beta;}public void show() {Console.WriteLine("alpha: {0}, beta: {1}", alpha, beta);}}class PassOb {public static void Main() {MyClass ob1 = new MyClass(4, 5);MyClass ob2 = new MyClass(6, 7);Console.Write("ob1: ");ob1.show();Console.Write("ob2: ");ob2.show();if(ob1.sameAs(ob2))Console.WriteLine("ob1 и оb2 имеют одинаковые значения.");elseConsole.WriteLine("ob1 и оb2 имеют разные значения.");Console.WriteLine();// Теперь делаем объект оb1 копией объекта оb2.ob1.copy(ob2);Console.Write("ob1 после копирования: ");}}ob1.show();if(ob1.sameAs(ob2))Console.WriteLine("ob1 и оb2 имеют одинаковые значения.");elseConsole.WriteLine("оb1 и оb2 имеют разные значения.");Выполнив эту программу, получаем такие результаты:ob1: alpha: 4, beta: 5ob2: alpha: 6, beta: 7оb1 и ob2 имеют разные значения.оb1 после копирования: alpha: 6, beta: 7оb1 и оb2 имеют одинаковые значения.Каждый из методов — sameAs() и сору() — принимает в качестве аргументаобъект.
Метод sameAs() сравнивает значения alpha и beta вызывающего объекта созначениями alpha и beta объекта, переданного в качестве аргумента ob. Этот метод188Часть I. Язык C#возвращает значение true только в том случае, если сравниваемые объекты содержатодинаковые значения в соответствующих переменных экземпляров. Метод сору()присваивает значения alpha и beta объекта, переданного в качестве аргумента ob,переменным экземпляра alpha и beta вызывающего объекта. Обратите внимание на то,что в обоих случаях в качестве типа параметра указан класс MyClass. Как видно из этойпрограммы, объекты (имеющие тип класса) передаются методам точно так же, как изначения встроенных типов.Как происходит передача аргументовВ предыдущем примере передача аргументов методу представляла собой простуюзадачу.
Однако существуют некоторые нюансы, которые там не были показаны. Вопределенных случаях результаты передачи объекта будут отличаться от результатовпередачи необъектных аргументов. Чтобы понять причину, необходимо рассмотреть двавозможных способа передачи аргументов.Первый способ называется вызовом по значению (call-by-value). В этом случаезначение аргумента копируется в формальный параметр метода. Следовательно, изменения,внесенные в параметр метода, не влияют на аргумент, используемый при вызове. Второйспособ передачи аргумента называется вызовом по ссылке (call-by-reference).
Здесь дляполучения доступа к реальному аргументу, заданному при вызове, используется ссылка нааргумент. Это значит, что изменения, внесенные в параметр, окажут воздействие нааргумент, использованный при вызове метода.При передаче методу значения нессылочного типа (например, int или double)имеет место вызов по значению. Таким образом, то, что происходит с параметром, которыйполучает аргумент, никак не влияет на данные вне метода. Рассмотрим следующуюпрограмму:// Демонстрация передачи простых типов по значению.using System;class Test {/* Этот метод не оказывает влияния на аргументы,используемые в его вызове.
*/public void noChange(int i, int j) {i = i + j;j = -j;}}class CallByValue {public static void Main() {Test ob = new Test();int a = 15, b = 20;Console.WriteLine("а и b перед вызовом: " + a + " " + b);ob.noChange(a, b);}}Console.WriteLine("а и b после вызова: " + a + " " + b);Глава 8. Подробнее о методах и классах189Результаты выполнения этой программы выглядят так:а и b перед вызовом: 15 20а и b после вызова: 15 20Как видите, операции, которые выполняются внутри метода noChange(), не влияютна значения а и b, используемые при вызове метода.При передаче методу ссылки на объект ситуация несколько усложняется. Строгоговоря, сама ссылка передается по значению, Таким образом, здесь выполняетсякопирование ссылки, после чего, как мы уже знаем, изменения, вносимые в параметр, неокажут влияния на аргумент.
(Например, если заставить параметр ссылаться на новыйобъект, это не коснется объекта, на который ссылается аргумент.) Однако (внимание, этоочень важно) изменения, вносимые в объект, на который ссылается параметр, повлияютсамым прямым образом на объект, на который ссылается аргумент. Давайте разберемся,почему так происходит.Вспомните, что при создании переменной типа класса вы создаете лишь ссылку наобъект. Следовательно, при передаче этой ссылки методу параметр, который получает ее,будет ссылаться на тот же объект, на который ссылается и аргумент. Это как раз означает,что объекты передаются методу посредством вызова по ссылке. Как следствие, изменения вобъекте внутри метода влияют на объект, используемый в качестве аргумента.
Рассмотрим,например, следующую программу:// Демонстрация передачи объектов по ссылке.using System;class Test {public int a, b;public Test(int i, int j) {a = i;b = j;}}/* Передаем объект. Теперь ob.a и ob.b в объекте,используемом при вызове, будут изменены. */public void change(Test ob) {ob.a = ob.a + ob.b;ob.b = -ob.b;}class CallByRef {public static void Main() {Test ob = new Test(15, 20);Console.WriteLine("ob.a и ob.b перед вызовом: " +ob.a + " " + ob.b);}}ob.change(ob);Console.WriteLine("ob.а и ob.b после вызова: " +ob.a + " " + ob.b);Эта программа генерирует такие результаты:ob.a и ob.b перед вызовом: 15 20ob.a и ob.b после вызова: 35 -20190Часть I.
Язык C#Как видите, в этом случае действия внутри метода change() влияют на объект,используемый в качестве аргумента.Итак, при передаче методу ссылки на объект сама ссылка передается посредствомвызова по значению, И поэтому делается копия этой ссылки. Но поскольку передаваемоезначение ссылается на некоторый объект, копия этого значения ссылается на тот же объект.Использование ref - и out-параметровВыше мы рассмотрели пример, в котором значения нессылочного типа (например,int или char) передавались методу по значению. И мы убедились в том, что изменения,вносимые в параметр, который получает значение нессылочного типа, не влияет нареальный аргумент, используемый при вызове метода. Однако такое поведение можноизменить.
Используя ключевые слова ref и out, можно передать значение любогонессылочного типа по ссылке. Тем самым мы позволим методу изменить аргумент,используемый при вызове.Прежде чем вникнуть в механизм использования ключевых слов ref и out, стоитпонять, когда может потребоваться передача нессылочного типа по ссылке. В общем случаесуществует две причины: позволить методу менять содержимое его аргументов иливозвращать более одного значения. Рассмотрим подробно каждую из причин.Часто программисту нужен метод, способный оперировать реальными аргументами,передаваемыми ему при вызове. Классическим примером служит метод swap(), которыйменяет местами значения двух аргументов.
При передаче значений нессылочного типа позначению невозможно написать метод обмена значениями двух аргументов, например типаint, используя действующий по умолчанию C#-механизм передачи параметров позначению. Эта проблема решается с помощью модификатора ref.Как вы знаете, инструкция return позволяет методу возвратить значение тому, ктосделал вызов. Однако метод может вернуть в результате одного вызова только однозначение. А как быть, если нужно вернуть два или больше значений? Например, нуженметод, который разбивает вещественное число на целую и дробную части.
Ведь в этомслучае метод должен возвратить два значения: целую часть и дробную составляющую.Такой метод невозможно написать, используя только одно возвращаемое значение. Вот сэтой проблемой и помогает справиться модификатор out.Использование модификатора refМодификатор параметра ref заставляет C# организовать вместо вызова по значениювызов по ссылке.
Модификатор ref используется при объявлении метода и его вызове.Рассмотрим простой пример. Следующая программа создает метод sqr(), которыйвозвращает квадрат целочисленного аргумента. Обратите внимание на использование ирасположение модификатора ref.// Использование модификатора ref для передачи// значения нессылочного типа по ссылке.using System;class RefTest {/* Этот метод изменяет свои аргументы.Обратите внимание на использование модификатора ref. */public void sqr(ref int i) {i = i * i;Глава 8. Подробнее о методах и классах191}}class RefDemo {public static void Main() {RefTest ob = new RefTest();int a = 10;}}Console.WriteLine("а перед вызовом: " + a);ob.sqr(ref a);// Обратите внимание//на использование модификатора ref.Console.WriteLine("а после вызова: " + a);Обратите внимание на то, что модификатор стоит в начале объявления параметра вметоде и предшествует имени аргумента при вызове метода.
Приведенные ниже результатывыполнения этой программы подтверждают, что значение аргумента а действительно быломодифицировано методом sqr().а перед вызовом: 10а после вызова: 100Используя модификатор ref, можно написать метод, который меняет значения двухаргументов нессылочного типа. Например, рассмотрим программу, которая содержит методswap(), меняющий значения двух целочисленных аргументов, передаваемых ему привызове.// Обмен значениями двух аргументов.using System;class Swap {// Этот метод меняет местами значения своих аргументов.public void swap(ref int a, ref int b) {int t;t = a;a = b;b = t;}}class SwapDemo {public static void Main() {Swap ob = new Swap();int x = 10, y = 20;Console.WriteLine("x и y перед вызовом: " +x + " " + y);ob.swap(ref x, ref y);}}192Console.WriteLine("x и y после вызова: " +x + " " + y);Часть I. Язык C#Вот результаты выполнения этой программы:x и y перед вызовом: 10 20x и y после вызова: 20 10И еще одно немаловажное замечание.
Аргументу, передаваемому методу “всопровождении” модификатора ref, должно быть присвоено значение до вызова метода.Дело в том, что, если метод получает такой аргумент, значит, параметр ссылается надействительное значение. Поэтому, используя модификатор ref, нельзя использоватьметод, присваивая его аргументу начальное значение.Использование модификатора outИногда приходится использовать ссылочный параметр не для передачи значенияметоду, а для его получения из метода. Например, может понадобиться метод, которыйвыполняет некоторую функцию, например открывает сетевой сокет, и возвращает код впараметре ссылочного типа, означающем удачное или неудачное выполнение этойоперации. В этом случае методу не нужно передавать какую бы то ни было информацию, ноот метода необходимо получить определенный результат.
Если воспользоватьсямодификатором ref, то мы должны инициализировать ref-параметр некоторымзначением до вызова метода. Таким образом, использование ref-параметра потребовалобы присвоения его аргументу фиктивного значения только для того, чтобы удовлетворитьэто требование.