Принципы объектно-ориентированного программирования

         

Параметры ref и out



Параметры ref и out

При попытке получения информации с помощью метода на С# вы получите только возвращаемое значение. Поэтому может показаться, что в результате вызова метода вы получите не более одного значения. Очевидно, что отдельно вызывать метод для каждой порции данных во многих ситуациях будет очень неуклюжим решением. Допустим, у вас есть класс Color, представляющий любой цвет в виде трех значений согласно стандарту RGB (красный-зеленый-синий). Использование лишь возвращаемых значений вынудит вас написать следующий код, чтобы получить все три значения:

// Предполагаем, что color - экземпляр класса Color, int red = color. GetRedQ; int green = color.GetGreenO; int blue = color. GetBlueQ;

Но нам хочется получить что-то вроде этого:



int red;

int green;

int blue;

color.GetRGB(red,green,blue);

Но при этом возникает проблема. При вызове метода color.GetRGB значения аргументов red, green и blue копируются в локальный стек метода, а переменные вызывающей функции остаются без изменений, сделанных методом.

На C++ эта проблема решается путем передачи при вызове метода указателей или ссылок на эти переменные, что позволяет методу обрабатывать данные вызывающей функции. Решение на С# выглядит аналогично. На самом деле С# предлагает два похожих решения. Первое из них использует ключевое слово ref. Оно сообщает компилятору С#, что передаваемые аргументы указывают на ту же область памяти, что и переменные вызывающего кода. Таким образом, если вызванный метод изменяет их и возвращает управление, переменные вызывающего кода также подвергнутся изменениям. Следующий код иллюстрирует использование ключевого слова ref на примере класса Color.

using System;

class Color {

public Color() {

this.red = 255; this.green = 0; this.blue = 125; }

protected int red; protected int green; protected int blue;

public void GetColors(ref int red, ref int green, ref int blue) {

red = this.red; green = this.green; blue = this.blue; > }

class RefTestlApp {

public static void MainQ

{

Color color = new ColorQ;

int red;

int green;

int blue;

color.GetColors(ref red, ref green, ref blue);

Console.WriteLine("red = {0}, green = {1}. blue = {2}", red, green, blue);

} }

У ключевого слова re/есть один недостаток, а приведенный код фактически не будет компилироваться. При использовании ключевого слова re/перед вызовом метода вы должны инициализировать передаваемые аргументы. Поэтому, чтобы этот код заработал, его нужно изменить:

using System;

class Color {

public ColorQ {

this.red = 255; this.green = 0; this.blue = 125; }

protected int red; protected int green; protected int blue;

public void GetColors(ref int red, ref int green, ref int blue)

<

red = this.red;

green = this.green; blue = this.blue; } }

class RefTest2App {

public static void MainQ {

Color color = new ColorO; int red = 0; int green = 0; int blue = 0;

color.GetColors(ref red, ref green, ref blue); Console. WriteLineC 1 red = {0}, green = {1}, blue = {2}", red, green, blue);

> }

He кажется ли вам, что инициализация переменных, которые позже будут перезаписаны, бессмысленна? Поэтому С# предоставляет альтернативный способ передачи аргументов, изменения значений которых должны быть видимыми вызывающему коду: с помощью ключевого слова out. Вот пример с тем же классом Color, где используется out

using System;

class Color {

public Color() {

this.red = 255; this.green = 0; this.blue = 125; }

protected int red;

protected int green;

protected int blue;

public void GetColors(out int red, out int green, out int blue) <

red = this.red;

green = this, green; blue = this.blue; } >

class OutTestlApp {

public static void MainQ {

Color color = new ColorO; int red; int green; int blue;

color.6etColors(out red, out green, out blue); Console.WriteLine("red = {0}, green = {1}, blue = {2}",

red, green, blue); } }

Единственное различие между ключевыми словами ref и out в том, что out не требует, чтобы вызывающий код сначала инициализировал передаваемые аргументы. А когда же применять ref! Когда нужна гарантия, что вызывающий код инициализировал аргумент. В приведенных примерах можно было применять ключевое слово out, так как вызываемый метод не зависит от значения передаваемой переменной. Ну а если вызываемый метод использует значение параметра? Взгляните:

using System;

class Window {

public Window(int x, int y) {

this.x = x; this.у = у; }

protected int x; protected int y;

public void Move(int x, int y) {

this.x = x; this,у = у; }

public void ChangePos(ref int x, ref int y) {

this.x += x;;

this.у += у;

x = this.x; у = this.у; } }

class OutTest2App {

public static void Main()

{

Window wnd = new Window(5, 5);

int x = 5; int у = 5;

wnd.ChangePos(ref x, ref y); Console.WriteLine("{0}, {1}", x, y);

x = -1; У = -1;

wnd.ChangePos(ref x, ref y); Console.WriteLine("{0}, {1}", x, y); } >

Как видите, работа вызываемого метода Window.Change Pos основана на переданных ему значениях. В данном случае ключевое слово ref вынуждает вызывающий код инициализировать эти значения, чтобы метод работал корректно.

 

Содержание раздела