C#. Параметры методов. Модификаторы ref и out. Примеры. Отличия между модификаторами ref и out




Параметры методов. Модификаторы ref и out. Примеры. Отличия между модификаторами ref и out


Содержание


Поиск на других ресурсах:

1. Применение модификатора параметра ref. Какое назначение модификатора параметра ref?

Модификатор ref предназначен для указания того, что параметр метода должен передаваться по ссылке а не по значению. Другими словами, если у методу нужно принудительно передать аргумент по ссылке, то при объявлении метода перед соответствующим формальным параметром нужно указать модификатор ref.

Модификатор параметра ref используется в описании формального параметра метода. Модификатор параметра ref указывается перед типом формального параметра, как показано ниже:

ref type param

Общая форма метода в классе, который получает формальный параметр ref следующая:

access return_type MethodName(ref type param)
{
    // ...
}

где

  • MethodName – имя метода;
  • access – тип доступа к методу (private, protected, public, internal);
  • return_type – тип, возвращаемый методом;
  • type – тип параметра с именем param, который получает метод;
  • param – имя формального параметра.

При вызове такого метода из другого кода, перед параметром также ставится модификатор ref:

MethodName(ref argument);

где argument – аргумент, который передается в метод. Этого требует синтаксис C#.

 

2. Примеры методов, которые используют модификатор параметра ref

Пример 1. Реализация метода, который умножает число на 5. Число есть входным параметром метода. Метод получает значение числа, умножает его на 5 и возвращает с помощью модификатора ref. То есть, метод увеличивает в 5 раз значение аргумента.

// метод, который увеличивает параметр x в 5 раз
public void Mult5(ref int x)
{
    x = x * 5;
}

Вызов метода Mult5() из другого программного кода

int d = 10;
qe.Mult5(ref d); // d = 10*5 = 50

После вызова, значение переменной d становится равным 50. Согласно синтаксису C#, перед вызовом переменной d нужно ставить модификатор параметра ref.



Пример 2. Решение квадратного уравнения. Задан класс QuadraticEquation, содержащий данные и метод решения квадратного уравнения.
В классе реализован метод Calc(), получающий два параметра x1, x2 с модификатором ref. Эти параметры изменяют свои значения в методе, в случае, если уравнение имеет решение.

// класс, который реализует данные и метод решения квадратного уравнения
class QuadraticEquation
{
    public double a, b, c;

    // конструктор класса, получает параметрами коэффициенты a, b, c уравнения
    public QuadraticEquation(double _a, double _b, double _c)
    {
        if (IsSolution(a, b, c))
        {
            a = _a;
            b = _b;
            c = _c;
        }
        else
            a = b = c = 0;
    }

    // внутренний метод, который определяет, имеет ли решение уравнение с задаными a,b,c
    bool IsSolution(double a, double b, double c)
    {
        double d = b * b - 4 * a * c;
        if (d < 0) return false;
            return true;
    }

    // Метод, который решает квадратное уравнение
    // Метод возвращает true и результат в x1, x2, если уравнение имеет решение,
    // в противном случае метод возвращает false
    public bool Calc(ref double x1, ref double x2)
    {
        // проверка, имеет ли уравнение решение
        if (!IsSolution(a, b, c))
            return false;

        // если решение есть, то вычисляются x1, x2
        double d = b * b - 4 * a * c;
        x1 = (-b - Math.Sqrt(d)) / (2 * a);
        x2 = (-b + Math.Sqrt(d)) / (2 * a);

        return true;
    }
}

Вызов метода из другого класса может быть таким как описан низшее

class Program
{
    static void Main(string[] args)
    {
        // создание объекта класса QuadraticEquation
        QuadraticEquation qe = new QuadraticEquation(2, -8, 5);

        double x1 = 0, x2 = 0;

        if (qe.Calc(ref x1, ref x2)) // вызов метода Calc(), перед x1, x2 задается модификатор ref
        {
            // если есть решение, то вывести корни уравнения x1, x2
            Console.WriteLine("x1 = {0}", x1);
            Console.WriteLine("x2 = {0}", x2);
        }
        else
            Console.WriteLine("Уравнение не имеет корней.");
        return;
    }
}

Следует заметить, что при вызове метода Calc() обязательно указываются модификаторы ref:

Calc(ref x1, ref x2)

Пример 3. Дана строка s. Разработать метод, который удаляет из строки s заданный символ c. Символ c есть входным параметром-значением. Строка s должна быть параметром-ссылкой и результатом.
Ниже приводится класс Str с реализацией метода DeleteSymbol(). Также продемонстрирован вызов метода DeleteSymbol() из функции Main().

class Str
{
    // метод удаляет из строки s символ c
    public void DeleteSymbol(ref string s, char c)
    {
        string s2="";

        for (int i=0; i<s.Length; i++)
            if (s[i] != c)
                s2 = s2 + s[i];
        s = s2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Str S = new Str();

        string str = "This is a textbook.";

        // вызов метода DeleteSymbol
        S.DeleteSymbol(ref str, 'i'); // str = "Ths s a textbook."

        Console.WriteLine(str);
        return;
    }
}

 

3. Применение модификатора параметра out. Какое назначение модификатора параметра out?

Модификатор параметра out используется, если необходимо выполнение двух условий:

  • методу не нужно передавать значение;
  • метод обязательно должен возвращать значение с помощью параметра.

Модификатор out для параметра с именем param типа type указывается в начале его объявления

out type param

Общая форма метода, который получает один параметр с модификатором out имеет вид

access return_type MethodName(out type param)
{
    // ...
}

где

  • MethodName – имя метода;
  • access – тип доступа к методу (private, protected, public, internal);
  • return_type – тип, который возвращает метод;
  • type – тип параметра с именем param, который получает метод;
  • param – имя формального параметра.

 

4. Примеры методов, которые используют модификатор параметра out

Пример 1. Разработать метод, который возвращает число Авогадро. Число Авогадро задается параметром метода.
Текст метода:

// число Авогадро
void GetAvogadro(out double Avg)
{
    Avg = 6.022140857e23;
}

Вызов метода из другого программного кода

// вызов метода GetAvogadro() из другого метода
double Avg;
GetAvogadro(out Avg); // указание модификатора out - обязательно
// Avg = 6.022140857E+23

Как видно из вышеприведенного кода, при вызове метода, который содержит out-параметр, обязательно нужно указывать модификатор out

GetAvogadro(out Avg);

Пример 2. Разработать метод CalcEquation(), который решает квадратное уравнение. Метод получает входными параметрами коэффициенты уравнения a, b, c. Эти параметры передаются в уравнение по значению.
Метод возвращает решение уравнения с помощью параметров x1, x2, которые объявлены с модификатором out.
Метод возвращает true с помощью оператора return, если уравнение имеет решение. В противном случае, метод возвращает false.
Ниже приведен класс Program, в котором реализован метод CalcEquation().

class Program
{
    static bool CalcEquation(double a, double b, double c, out double x1, out double x2)
    {
        double d;
        d = b * b - 4 * a * c;
        if (d>=0)
        {
            x1 = (-b - Math.Sqrt(d)) / (2 * a);
            x2 = (-b + Math.Sqrt(d)) / (2 * a);
            return true;
        }
        else
        {
            x1 = x2 = 0; // обязательно, иначе ошибка
            return false;
        }
    }

    static void Main(string[] args)
    {
        // Демонстрация использования модификатора параметра out
        bool res;
        double x1, x2;

        // вызов метода
        res = CalcEquation(8, 3, -4, out x1, out x2);

        if (res)
        {
            Console.WriteLine("x1 = {0}", x1);
            Console.WriteLine("x2 = {0}", x2);
        }
        else
            Console.WriteLine("Уравнение не имеет корней");
        return;
    }
}

Пример 3. Разработать метод, который возвращает название цифры в строке.

// класс, который реализует метод, возвращающий название цифры
class Number
{
    // вывод названия числа на основе входного значения num
    public void TextNumber(int num, out string  text)
    {
        switch (num)
        {
            case 1: text = "one"; break;
            case 2: text = "two"; break;
            case 3: text = "three"; break;
            case 4: text = "four"; break;
            case 5: text = "five"; break;
            case 6: text = "six"; break;
            case 7: text = "seven"; break;
            default: text = ""; break;
        }
        return;
    }
}

Использование метода TextNumber() класса Number

Number nm = new Number(); // nm - объект класса Number
string s;

nm.TextNumber(3, out s); // s = "three"
nm.TextNumber(8, out s); // s = ""
nm.TextNumber(1, out s); // s = "one'

 

5. Какие существуют отличия между модификаторами ref и out?

Между модификаторами ref и out есть три взаимосвязанных отличия:

  • 1. Параметр с модификатором out используется только для возвращения значения из метода. Параметр с модификатором ref может использоваться и для возвращения и для установки значения в методе другим переменным. Поэтому, перед вызовом метода, нет потребности присваивать какое-то значение переменной, которая используется с модификатором out.
  • 2. В методе, переменная объявленная с параметром out считается неинициализированной. Переменная, объявленная с ref считается инициализированной. Поэтому, нельзя использовать out-переменную в правой части оператора присваивания. А ref-переменную можно.
  • 3. Если параметр объявлен с модификатором out, то в теле метода этому параметру обязательно должно быть присвоено какое-то значение. Иначе будет ошибка компиляции. Если параметр объявлен с модификатором ref, то этому параметру присваивать значения в теле метода не обязательно.

 

6. Передача в функцию ссылки на экземпляр класса с использованием модификатора ref. Пример

В функцию можно передавать не только значение базовых типов (int, double, char и т.д.) но и значение экземпляров класса (объектов). Если при передаче в функцию объекта класса используются модификаторы ref или out, то сама ссылка передается по ссылке. Это позволяет изменять сам объект в методе.

Пример. В примере реализован метод, который заменяет один объект класса на другой. Оба объекта класса передаются в метод как параметры. Объект, который нужно заменить, передается с модификатором ref.

using System;
using static System.Console;

namespace ConsoleApp2
{
  // Класс Point - описывает точку на координатной плоскости
  class Point
  {
    // внутренние поля класса
    int x;
    int y;

    // Конструктор
    public Point(int x, int y)
    {
      this.x = x;
      this.y = y;
    }

    // Метод присваивания одного экземпляра класса другому:
    // point1 <= point2
    public void AssignmentPoint(ref Point point1, Point point2)
    {
      point1 = point2; // переопределение объекта point1
    }

    // Метод, который выводит значения полей класса
    public void Print(string text)
    {
      Write(text);
      WriteLine("x = {0}, y = {1}", x, y); 
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // Передача ссылок на объекты как ref- и out-параметров
      // 1. Объявить экземпляры классов
      Point pt1 = new Point(1, 2);
      Point pt2 = new Point(5, 6);

      // 2. Вывести экземпляры классов
      pt1.Print("Object pt1:");
      pt2.Print("Object pt2:");

      // 3. Вызов метода AssignmentPoint() - передача объекта как ref-ссылки
      pt1.AssignmentPoint(ref pt1, pt2); // pt1 <= pt2

      // 4. Вывести экземпляры классов после присваивания 
      WriteLine("\nAfter AssignmentPoint");
      pt1.Print("Object pt1:");
      pt2.Print("Object pt2:");
    }
  }
}

Результат выполнения программы

Object pt1:x = 1, y = 2
Object pt2:x = 5, y = 6

After AssignmentPoint
Object pt1:x = 5, y = 6
Object pt2:x = 5, y = 6

Проанализировав результат работы программы можно прийти к заключению. В методе AssignmentPoint() состоялась полная замена объекта pt1 на объект pt2. Это возможно благодаря модификатору ref. Если убрать модификатор ref с первого параметра метода AssignmentPoint(), тогда изменения внутри метода не будут действовать в вызывающем коде (функции main()). Чтобы проверить это, достаточно убрать модификатор ref в методе AssignmentPoint() и в вызове этого метода.

 

7. Передача в функцию ссылки на экземпляр класса с использованием модификатора out. Пример

Экземпляр класса может быть передан в функцию с модификатором out. При такой передаче можно изменять сам экземпляр класса как и в случае с ref-модификатором.

Пример. В примере реализован метод FormArray(), формирующий новый объект класса ArrayDouble, выделяя для него память и заполняя произвольным значением. В вызывающем коде (функции main()) метод получает экземпляр класса со значением null.

using System;
using static System.Console;

namespace ConsoleApp2
{
  // Класс, реализующий массив чисел
  class ArrayDouble
  {
    private double[] A; // массив чисел

    // Конструктор с 1 параметром
    public ArrayDouble(int length)
    {
      // выделить память для массива и заполнить его нулевыми значениями
      A = new double[length];
      for (int i = 0; i < A.Length; i++)
      {
        A[i] = 0.0;
      }
    }

    // Методы доступа
    public double GetAi(int index)
    {
      if ((index >= 0) && (index < A.Length))
        return A[index];
      else
        return 0;
    }

    public void SetAi(int index, double value)
    {
      if ((index >= 0) && (index < A.Length))
        A[index] = value;
    }

    // Метод, который выводит массив в строку
    public void Print(string text)
    {
      WriteLine(text);
      for (int i = 0; i < A.Length; i++)
      {
        Write("{0:f2}\t", A[i]);
      }
      WriteLine();
    }
  }

  class Program
  {
    // Метод, который заполняет массив типа ArrayDouble случайными числами,
    // метод формирует полностью новый экземпляр класса ArrayDouble
    static void FormArray(out ArrayDouble A, int length)
    {
      // 1. Инициализировать генератор случайных чисел
      Random rnd_num = new Random();

      // 2. Выделить память для экземпляра A
      A = new ArrayDouble(length);

      // 3. Заполнить экземпляр A случайными числами от 0 до 10
      for (int i = 0; i < length; i++)
      {
        A.SetAi(i, rnd_num.NextDouble() * 10);
      }
    }

    static void Main(string[] args)
    {
      ArrayDouble ad = null; // экземпляр класса ArrayDouble

      // Сформировать массив ad
      // параметр out - массив формируется внутри метода FormArray()
      Program.FormArray(out ad, 5);
      ad.Print("1 - Array ad:");

      // Повторно сформировать массив ad
      FormArray(out ad, 7); // на выходе из метода - новый экземпляр
      ad.Print("2 - Array ad:");
    }
  }
}

Результат выполнения программы

1 - Array ad:
4.67 8.74 2.64 4.83 0.34
2 - Array ad:
6.65 6.66 4.32 1.24 8.30 9.73 3.13

 


Связанные темы