Добавить в цитаты Настройки чтения

Страница 46 из 188

class CallByValue { 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); }}Вот какой результат дает выполнение этой программы.

а и b до вызова: 15 20а и b после вызова: 15 20Как видите, операции, выполняемые в методе NoChange(), не оказывают никакоговлияния на значения аргументов а и b, используемых для вызова данного метода. Этоопять же объясняется тем, что параметрам i и j переданы копии значений аргументова и b, а сами аргументы а и b совершенно не зависят от параметров i и j. В частности,присваивание параметру i нового значения не будет оказывать никакого влияния нааргумент а.Дело несколько усложняется при передаче методу ссылки на объект. В этом случаесама ссылка по-прежнему передается по значению. Следовательно, создается копияссылки, а изменения, вносимые в параметр, не оказывают никакого влияния на аргумент. (Так, если организовать ссылку параметра на новый объект, то это изменение неповлечет за собой никаких последствий для объекта, на который ссылается аргумент.)Но главное отличие вызова по ссылке заключается в том, что изменения, происходящие с объектом, на который ссылается параметр, окажут влияние на тот объект, накоторый ссылается аргумент. Попытаемся выяснить причины подобного влияния.Напомним, что при создании переменной типа класса формируется только ссылка на объект. Поэтому при передаче этой ссылки методу принимающий ее параметрбудет ссылаться на тот же самый объект, на который ссылается аргумент. Это означает,что и аргумент, и параметр ссылаются на один и тот же объект и что объекты, по существу, передаются методам по ссылке. Таким образом, объект в методе будет оказыватьвлияние на объект, используемый в качестве аргумента. Для примера рассмотрим следующую программу.

// Передача объектов по ссылке.using System;

class Test { public int a, b; public Test(int i, int j) { a = i; b = j; } / Передать объект. Теперь переменные ob.а и ob.b из объекта, используемого в вызове метода, будут изменены. / public void Change(Test ob) { ob.a = ob.a + ob.b; ob.b = -ob.b; }}

class CallByRef { static void Main() { Test ob = new Test(15, 20); Console.WriteLine("ob.а и 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 -20Как видите, действия в методе Change() оказали в данном случае влияние наобъект, использовавшийся в качестве аргумента.Итак, подведем краткий итог. Когда объект передается методу по ссылке, сама ссылка передается по значению, а следовательно, создается копия этой ссылки. Но эта копия будет по-прежнему ссылаться на тот же самый объект, что и соответствующий аргумент. Это означает, что объекты передаются методам неявным образом по ссылке.## Использование модификаторов параметров ref и outКак пояснялось выше, аргументы простых типов, например int или char, передаются методу по значению. Это означает, что изменения, вносимые в параметр, принимающий значение, не будут оказывать никакого влияния на аргумент, используемыйдля вызова. Но такое поведение можно изменить, используя ключевые слова ref и outдля передачи значений обычных типов по ссылке. Это позволяет изменить в самомметоде аргумент, указываемый при его вызове.Прежде чем переходить к особенностям использования ключевых слов ref и out,полезно уяснить причины, по которым значение простого типа иногда требуется передавать по ссылке. В общем, для этого существуют две причины: разрешить методуизменить содержимое его аргументов или же возвратить несколько значений. Рассмотрим каждую из этих причин более подробно.Нередко требуется, чтобы метод оперировал теми аргументами, которые ему передаются. Характерным тому примером служит метод Swap(), осуществляющий перестановку значений своих аргументов. Но поскольку аргументы простых типов передаются по значению, то, используя выбираемый в C# по умолчанию механизм вызовапо значению для передачи аргумента параметру, невозможно написать метод, меняющий местами значения двух его аргументов, например типа int. Это затруднение разрешает модификатор ref.Как вам должно быть уже известно, значение возвращается из метода вызывающейчасти программы с помощью оператора return. Но метод может одновременно возвратить лишь одно значение. А что, если из метода требуется возвратить два или болеефрагментов информации, например, целую и дробную части числового значения сплавающей точкой? Такой метод можно написать, используя модификатор out.### Использование модификатора параметра refМодификатор параметра ref принудительно организует вызов по ссылке, а не позначению. Этот модификатор указывается как при объявлении, так и при вызове метода. Для начала рассмотрим простой пример. В приведенной ниже программе создается метод Sqr(), возвращающий вместо своего аргумента квадрат его целочисленногозначения. Обратите особое внимание на применение и местоположение модификатора ref.

// Использовать модификатор ref для передачи значения обычного типа по ссылке.using System;

class RefTest { // Этот метод изменяет свой аргумент. Обратите // внимание на применение модификатора ref. public void Sqr(ref int i) { i = i * i; }}



class RefDemo { static void Main() { RefTest ob = new RefTest(); int a = 10; Console.WriteLine("а до вызова: " + a); ob.Sqr(ref a); // обратите внимание на применение модификатора ref Console.WriteLine("а после вызова: " + а); }}Как видите, модификатор ref указывается перед объявлением параметра в самомметоде и перед аргументом при вызове метода. Ниже приведен результат выполненияданной программы, который подтверждает, что значение аргумента а действительнобыло изменено с помощью метода Sqr().

а до вызова: 10а после вызова: 100Теперь, используя модификатор ref, можно написать метод, переставляющий местами значения двух своих аргументов простого типа. В качестве примера ниже приведена программа, в которой метод Swap() выполняет перестановку значений двухсвоих целочисленных аргументов, когда он вызывается.

// Поменять местами два значения.using System;

class ValueSwap { // Этот метод меняет местами свои аргументы. public void Swap(ref int a, ref int b) { int t; t = a; a = b; b = t; }}

class ValueSwapDemo { static void Main() { ValueSwap ob = new ValueSwap(); int x = 10, у = 20; Console.WriteLine("x и у до вызова: " + х + " " + у); ob.Swap(ref х, ref у); Console.WriteLine("х и у после вызова: " + х + " " + у); }}Вот к какому результату приводит выполнение этой программы.

х и у до вызова: 10 20х и у после вызова: 20 10В отношении модификатора ref необходимо иметь в виду следующее. Аргументу,передаваемому по ссылке с помощью этого модификатора, должно быть присвоенозначение до вызова метода. Дело в том, что в методе, получающем такой аргумент вкачестве параметра, предполагается, что параметр ссылается на действительное значение. Следовательно, при использовании модификатора ref в методе нельзя задатьпервоначальное значение аргумента.### Использование модификатора параметра outИногда ссылочный параметр требуется использовать для получения значения изметода, а не для передачи ему значения. Допустим, что имеется метод, выполняющийнекоторую функцию, например, открытие сетевого сокета и возврат кода успешного или неудачного завершения данной операции в качестве ссылочного параметра.В этом случае методу не передается никакой информации, но в то же время он долженвозвратить определенную информацию. Главная трудность при этом состоит в том,что параметр типа ref должен быть инициализирован определенным значением довызова метода. Следовательно, чтобы воспользоваться параметром типа ref, придетсязадать для аргумента фиктивное значение и тем самым преодолеть данное ограничение. Правда, в C# имеется более подходящий вариант выхода из подобного затруднения — воспользоваться модификатором параметра out.Модификатор параметра out подобен модификатору ref, за одним исключением: он служит только для передачи значения за пределы метода. Поэтому переменной, используемой в качестве параметра out, не нужно (да и бесполезно) присваивать какое-то значение. Более того, в методе параметр out считается неинициализированным, т.е. предполагается, что у него отсутствует первоначальное значение. Этоозначает, что значение должно быть присвоено данному параметру в методе до егозавершения. Следовательно, после вызова метода параметр out будет содержать некоторое значение.Ниже приведен пример применения модификатора параметра out. В этом примере программы для разделения числа с плавающей точкой на целую и дробную частииспользуется метод GetParts() из класса Decompose. Обратите внимание на то, каквозвращается каждая часть исходного числа.