Модификаторы параметров ref и out

В C# любой тип является либо ссылочным, либо значимым типом. При работе со значением ссылочного типа, мы фактически всегда работаем с ссылкой на это значение. А в случае с типом значения мы работаем напрямую со значением. Отсюда следует что оператор присвоения копирует ссылку на значение для ссылочного типа и само значение для значимого.

Например int – это значимый тип. А любой тип массива – ссылочный.

Что выводится в консоль?

Фрагмент 1.25

int a = 0;
int b = a;
b = 10;
a = 15;

Console.WriteLine(a);
Console.WriteLine(b);

Правильный ответ: 10 и 15. И в этом нет ничего странного так, как в случае с типом значения (которым является тип int) при присвоение одной переменной другой, копируется значение переменной и в данном случае это само значения. Соответственно в один момент переменная b и a имеют одинаковое значение но это разные экземпляры одного значения так, что изменение одной переменной не приводит к изменению другой.

А что будет в этом случае?

Фрагмент 1.26

int[] array1 = new int[] { 0, 0 };
int[] array2 = array1;

array2[0] = 10;
array1[1] = 15;


Console.WriteLine(array1[0]);
Console.WriteLine(array1[1]);

Console.WriteLine(array2[0]);
Console.WriteLine(array2[1]);

Правильный ответ: 10 15 10 15

Переменные, с типом массива, содержат в себе ссылку на значение и при присваивании одной переменной к значению другой, мы копируем ссылку на один и тот же массив в памяти.


Всё это справедливо и для вызовов методов. Когда мы указываем переменную в качестве аргумента метода, мы просто берём значение переменной и передаем ее в метод.

Как думаете, что будет в консоли?

Фрагмент 1.28

static void Main(string[] args)
{
    int a = 15;
    int b = 10;
    int c = 0;

    Sum(a, b, c);

    Console.WriteLine(c);
}

static void Sum(int a, int b, int c)
{
    c= a + b;
}

Ответ ноль. Во-первых переменная “c” в методе Main и параметр “c” метода Sum никак не связаны. Во-вторых мы передаем в метод копию значения переменной c. Также хочу заметить что если вам нужно узнать в точке вызова результат работы метода, то для этого существует возвращаемое значение.

Фрагмент 1.29

static void Main(string[] args)
{
    int a = 15;
    int b = 10;
    int c = 0;

    c = Sum(a, b);

    Console.WriteLine(c);
}

static int Sum(int a, int b)
{
    return a + b;
}

Идём далее. А как вы думаете что будет если передать массив в метод? Так как он ссылочный, передаваться будет копия ссылки на массив в памяти. Ну и контрольный вопрос. Что мы увидим в консоли?

Фрагмент 1.30

static void Main(string[] args)
{
    char[] map = new char[] { '_', '_', '!', '_', '_', '_'};

    Generate(map);

    for (int i = 0; i < map.Length; i++)
    {
        Console.Write(map[i]);
    }
}

static void Generate(char[] map)
{
    for(int i = 0; i < map.Length; i++)
    {
        map[i] = '#';
    }
}

Как не странно линию из решёток.

Розы гибнут на газонах

А шпана на красных зонах

У вас должен возникнуть вполне резонный вопрос. Зачем я об этом рассказываю? В заголовке же написано “Модификаторы ref и out”. А ответ простой, весь этот текст рассказывает о том, что в метод нельзя передавать переменную. Что в него передается копия значения переменной.

И тут я такой хоба, достаю из широких штанин модификаторы которые… позволяют передавать ссылку на переменную и изменять её внутри метода. Давайте вспомним наш предыдущий пример с методом Sum и переменной “c” и попробуем выразить его таким образом, чтобы параметр “c” реально был привязан.

Фрагмент 1.31

static void Main(string[] args)
{
    int a = 15;
    int b = 10;
    int c = 0;

    Sum(a, b, ref c);

    Console.WriteLine(c);
}

static void Sum(int a, int b, ref int c)
{
    c = a + b;
}

При таком исполнение мы получаем действительно значение 25 в консоли потому, что указали что параметр “c” – это ссылка на какую-то переменную. И при вызове метода нам нужно указать какую-то переменную с тем же модификатором в качестве аргумента. После этого внутри метода изменяя параметр “c” мы изменяем внешнюю переменную.

Важно заметить что имя переменной и параметра может не совпадать.

Помимо модификатор ref у нас так же есть out. Технически они работают одинаково, но разность у них семантическая. Так ref говорит только о том, что будет ссылка на некую переменную которая должна иметь значение до вызова. А out говорит что переменная может быть пустая и метод обязан задать ей значение.

Например в методе Sum лучше было бы использовать out так, как метод Sum гарантирует, что в результате своего выполнения связанная переменная будет проинициализирована, а также ему не требуется какое-то изначальное значение переданной переменной.  

Фрагмент 1.32

static void Main(string[] args)
{
    int a = 15;
    int b = 10;
    int c;

    Sum(a, b, out c);

    Console.WriteLine(c);
}

static void Sum(int a, int b, out int c)
{
    c = a + b;
}

Если вы нашли ошибку, пожалуйста выделите её и нажмите Ctrl+Enter.


Leave a Reply

Your email address will not be published. Required fields are marked *