Поиск

Параметры 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 вынуждает вызывающий код инициализировать эти значения, чтобы метод работал корректно.