Neitralov Neitralov

Привет, я Максим Осин

Backend • Frontend • DevOps

Россия, г. Казань

Все способы получить более одного значения из функции в C#

15 августа 2026 г.


Вернуть значение из функции или все же из метода?

Внезапное пояснение терминологии.

В силу того, что C# — это объектно-ориентированный язык, в нем именованные блоки кода называются методами. Сам термин «метод» закреплен за функцией (или процедурой), которая является членом класса или структуры. Тем самым метод всегда «принадлежит» какому-то объекту.

Термины «процедура» и «функция» пришли из структурных языков программирования:

  • Функция: блок кода, который обязательно возвращает значение вызывающему коду.
  • Процедура: блок кода, который НЕ возвращает значение вызывающему коду.

Поскольку для C# это все «методы», то деление довольно условное:

  • Метод, который возвращает значение — функция.
  • Метод, который объявлен с ключевым словом void — процедура.

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

Четыре основных способа вернуть более одного значения из функции

В C# мы объявляем функцию плюс-минус следующим образом.

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

Это пример обычной функции, которая принимает аргументы в виде двух целочисленных значений и возвращает их сумму. На позиции возвращаемого значения указан тип int, который сигнализирует нам, что мы возвращаем только одно значение и это целое число.

Но бывают ситуации, когда из функции необходимо вернуть более одного значения. В C# есть четыре основных способа как это сделать. Каждый способ имеет свои особенности, которые влияют на выбор в зависимости от ситуации.

1. Вернуть экземпляр класса или структуры

Самый популярный способ вернуть несколько значений.

Для начала объявляем какой-нибудь класс, в котором при помощи полей и/или свойств перечисляем все его содержимое.

class Person
{
    string Name { get; set; }
    int Age { get; set; }
}

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

Person GetKolya()
{
    return new Person 
    {
        Name = "Коля",
        Age = 18
    }
}

Это вариант, который обычно стоит выбрать, если необходимо вернуть набор данных разного типа, объединяемых по смыслу.

Типичные сценарии использования:

  1. Выполнить какую-то обработку над объектом и вернуть его новое состояние.
  2. Создать новый объект из набора входных параметров («Simple Factory»).
  3. Определить DTO, чтобы вернуть какое-то ограниченное представление об объекте (например, из базы данных).

2. Вернуть коллекцию

Представим, что мы хотим создать единичную матрицу заданного размера. Для этого мы можем использовать массивы.

int[,] CreateIdentityMatrix(int n)
{
    var matrix = new int[n, n];
    
    for (int i = 0; i < n; i++)
    {
        matrix[i, i] = 1;
    }
    
    return matrix;
}

Разумеется, ситуации не ограничиваются математическими задачами: на месте массива могла быть любая другая коллекция.

Это вариант, который обычно стоит выбрать, если необходимо вернуть набор однородных данных, где все элементы одного типа.

Типичные сценарии использования:

  1. Собрать структуру данных по заданному алгоритму (например, игровое поле для игры «Сапер» с расставленными минами).
  2. Вернуть список однородных элементов (собрать на месте, из другой коллекции или из базы данных).

3. Вернуть кортеж

Начиная с C# 7.0, появились удобные кортежи (System.ValueTuple). Они мне очень нравятся, например, за возможность менять значения переменных местами в одну строку. Практического смысла в этом мало, но это выглядит красиво в решениях на LeetCode.

(int A, int B) SpawValues(int a, int b)
{
    return (b, a)
}

Это вариант, который обычно стоит выбрать, если мы не хотим создавать для наших значений новый класс, а результат целиком не переиспользуется в других местах.

Типичные сценарии использования:

  1. Вернуть несколько значений из функции, для которых создавать отдельную структуру слишком «громоздко».

4. Вернуть дополнительное значение с модификатором параметра out

Нередко возникает необходимость сначала проверить значение и, если оно проходит проверку, выполнить с ним различные манипуляции.

В этом плане моим любимым примером является функция TryGetComponent из Unity. Приведу пример ее упрощенной реализации.

bool TryGetComponent<T>(out T component)
{
    // Ищем компонент нужного типа
    // _components — список компонентов, который объявлен где-то в классе...
    foreach (var component in _components)
    {
        if (component is T typedComponent)
        {
            component = typedComponent;
            return true;
        }
    }
    
    component = default;
    return false;
}

В одной из моих игр (Cosmic Bustle) именно таким образом делаются проверки в компоненте, который отвечает за обработку столкновений игрока с объектами в игровом мире.

Проверки в подобном стиле хороши своей компактностью, потому что можно проверить, обладает ли некий объект, с которым столкнулся игрок, необходимым компонентом, который бы его однозначно определял (например, все враги в мире имеют компонент Enemy), и сразу выполнить некую логику, если объект оказался нужного типа.

if (gameObject.TryGetComponent<Enemy>(out enemy))
{
    // Логика взаимодействия с врагом
}

Иначе бы конструкция выглядела как-то так:

Enemy enemy = object.Components.FirstOrDefault(x => x is Enemy);

if (enemy != null)
{
    // Логика взаимодействия с врагом
}

Выше уже разобран типичный сценарий использования, так что на этом и остановимся.