При попытке получения информации с помощью метода на С# вы получите только возвращаемое значение. Поэтому может показаться, что в результате вызова метода вы получите не более одного значения. Очевидно, что отдельно вызывать метод для каждой порции данных во многих ситуациях будет очень неуклюжим решением. Допустим, у вас есть класс
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
вынуждает вызывающий код инициализировать эти значения, чтобы метод работал корректно.