ref (Справочник по C#)ref (C# Reference)

Ключевое слово ref указывает на значение, переданное по ссылке.The ref keyword indicates a value that is passed by reference. Оно используется в четырех разных контекстах:It is used in four different contexts:

  • В сигнатуре и вызове метода для передачи аргумента в метод по ссылке.In a method signature and in a method call, to pass an argument to a method by reference. Дополнительные сведения см. в статье о передаче аргументов по ссылке.For more information, see Passing an argument by reference.
  • В сигнатуре метода для возврата значения вызывающему объекту по ссылке.In a method signature, to return a value to the caller by reference. Дополнительные сведения см. в разделе Значения, возвращаемые по ссылке.For more information, see Reference return values.
  • В теле элемента для указания на то, что возвращаемые ссылочные значения хранятся локально в виде ссылки, которая может быть изменена вызывающим объектом, или, в общем случае, что локальная переменная обращается к другому значению по ссылке.In a member body, to indicate that a reference return value is stored locally as a reference that the caller intends to modify or, in general, a local variable accesses another value by reference. Дополнительные сведения см. в статье о ссылочных локальных переменных.For more information, see Ref locals.
  • В объявлении struct, чтобы объявить ref struct или ref readonly struct.In a struct declaration to declare a ref struct or a ref readonly struct. Дополнительные сведения см. в статье о типах ссылочных структур.For more information, see ref struct types.

Передача аргументов по ссылкеPassing an argument by reference

При использовании в списке параметров метода ключевое слово ref указывает на то, что аргумент передается по ссылке, а не по значению.When used in a method's parameter list, the ref keyword indicates that an argument is passed by reference, not by value. Эффект передачи по ссылке в том, что все изменения аргумента в вызываемом методе отражаются в вызывающем методе.The effect of passing by reference is that any change to the argument in the called method is reflected in the calling method. Например, если вызывающий объект передает выражение локальной переменной или выражение доступа к элементу массива и вызванный метод заменяет объект, на который ссылается параметр ref, то локальная переменная или элемент массива взывающего объекта будет ссылаться на новый объект после возврата из метода.For example, if the caller passes a local variable expression or an array element access expression, and the called method replaces the object to which the ref parameter refers, then the caller’s local variable or the array element now refers to the new object when the method returns.

Примечание

Не следует путать понятие передачи по ссылке с понятием ссылочных типов.Do not confuse the concept of passing by reference with the concept of reference types. Эти два понятия не совпадают.The two concepts are not the same. Параметр метода может быть изменен с помощью ref независимо от того, принадлежит ли он к типу значения или ссылочному типу.A method parameter can be modified by ref regardless of whether it is a value type or a reference type. При передаче по ссылке упаковка-преобразование типа значения не производится.There is no boxing of a value type when it is passed by reference.

Для использования параметра ref и при определении метода, и при вызове метода следует явно использовать ключевое слово ref, как показано в следующем примере.To use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword, as shown in the following example.

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

Аргумент, передаваемый в параметр ref или in, нужно инициализировать перед передачей.An argument that is passed to a ref or in parameter must be initialized before it is passed. В этом заключается отличие от параметров out, аргументы которых не требуют явной инициализации перед передачей.This differs from out parameters, whose arguments do not have to be explicitly initialized before they are passed.

Члены класса не могут иметь сигнатуры, отличающихся только ref, in или out.Members of a class can't have signatures that differ only by ref, in, or out. Если единственное различие между двумя членами типа состоит в том, что один из них имеет параметр ref, а второй — параметр out или in, возникает ошибка компилятора.A compiler error occurs if the only difference between two members of a type is that one of them has a ref parameter and the other has an out, or in parameter. Например, следующий код не будет компилироваться.The following code, for example, doesn't compile.

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded 
    // methods that differ only on ref and out".
    public void SampleMethod(out int i) { }
    public void SampleMethod(ref int i) { }
}

Но методы можно перегружать, если один метод имеет параметр ref, in или out, а другой — параметр по значению, как показано в приведенном ниже примере.However, methods can be overloaded when one method has a ref, in, or out parameter and the other has a value parameter, as shown in the following example.

class RefOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(ref int i) { }
}

В других ситуациях, требующих соответствия сигнатур, таких как скрытие или переопределение, in, ref и out являются частью сигнатуры и не соответствуют друг другу.In other situations that require signature matching, such as hiding or overriding, in, ref, and out are part of the signature and don't match each other.

Свойства не являются переменными.Properties are not variables. Они являются методами и не могут быть переданы в параметр ref.They are methods, and cannot be passed to ref parameters.

Ключевые слова ref, in и out запрещено использовать для следующих типов методов.You can't use the ref, in, and out keywords for the following kinds of methods:

  • Асинхронные методы, которые определяются с помощью модификатора async.Async methods, which you define by using the async modifier.
  • Методы итератора, которые включают оператор yield return или yield break.Iterator methods, which include a yield return or yield break statement.

Пример передачи аргументов по ссылкеPassing an argument by reference: An example

В предыдущих примерах типы значений передаются по ссылке.The previous examples pass value types by reference. Также можно использовать ключевое слово ref для передачи ссылочных типов по ссылке.You can also use the ref keyword to pass reference types by reference. Передача ссылочного типа по ссылке позволяет вызываемому методу заменять объект, на который указывает ссылочный параметр в вызывающем объекте.Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller. Место хранения объекта передается методу в качестве значения ссылочного параметра.The storage location of the object is passed to the method as the value of the reference parameter. Если изменить место хранения параметра (с указанием на новый объект), необходимо изменить место хранения, на который ссылается вызывающий объект.If you change the value in the storage location of the parameter (to point to a new object), you also change the storage location to which the caller refers. В следующем примере экземпляр ссылочного типа передается как параметр ref.The following example passes an instance of a reference type as a ref parameter.

class Product
{
    public Product(string name, int newID)
    {
        ItemName = name;
        ItemID = newID;
    }

    public string ItemName { get; set; }
    public int ItemID { get; set; }
}

private static void ModifyProductsByReference()
{
    // Declare an instance of Product and display its initial values.
    Product item = new Product("Fasteners", 54321);
    System.Console.WriteLine("Original values in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);

    // Pass the product instance to ChangeByReference.
    ChangeByReference(ref item);
    System.Console.WriteLine("Back in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);
}

private static void ChangeByReference(ref Product itemRef)
{
    // Change the address that is stored in the itemRef parameter.   
    itemRef = new Product("Stapler", 99999);

    // You can change the value of one of the properties of
    // itemRef. The change happens to item in Main as well.
    itemRef.ItemID = 12345;
}

Дополнительные сведения о передаче ссылочных типов по значению и по ссылке см. в разделе Передача параметров ссылочного типа.For more information about how to pass reference types by value and by reference, see Passing Reference-Type Parameters.

Возвращаемые ссылочные значенияReference return values

Возвращаемые ссылочные значения — это значения, которые метод возвращает вызывающему объекту по ссылке.Reference return values (or ref returns) are values that a method returns by reference to the caller. Это значит, что вызывающий объект может изменять значение, возвращаемое методом, и это изменение будет отражаться в состоянии объекта, содержащего этот метод.That is, the caller can modify the value returned by a method, and that change is reflected in the state of the object that contains the method.

Возвращаемое ссылочное значение определяется с помощью ключевого слова ref:A reference return value is defined by using the ref keyword:

  • В сигнатуре метода.In the method signature. Например, следующая сигнатура указывает, что метод GetCurrentPrice возвращает значение Decimal по ссылке.For example, the following method signature indicates that the GetCurrentPrice method returns a Decimal value by reference.
public ref decimal GetCurrentPrice()
  • Между токеном return и переменной, возвращенной в инструкции return в методе.Between the return token and the variable returned in a return statement in the method. Например:For example:
return ref DecimalArray[0];

Чтобы вызывающий объект имел возможность изменять состояние объекта, возвращаемое ссылочное значение должно храниться в переменной, которая явно определена как ссылочная локальная переменная.In order for the caller to modify the object's state, the reference return value must be stored to a variable that is explicitly defined as a ref local.

Вызываемый метод может также объявить возвращаемое значение ref readonly, чтобы возвращать значения по ссылке, и запретить вызывающему коду изменять возвращенное значение.The called method may also declare the return value as ref readonly to return the value by reference, and enforce that the calling code cannot modify the returned value. Вызывающий метод может обойтись без копирования возвращаемого значения, сохраняя его в локальной переменной ref readonly.The calling method can avoid copying the returned valued by storing the value in a local ref readonly variable.

Пример см. в разделе Пример использования возвращаемых ссылочных значений и ссылочных локальных переменных.For an example, see A ref returns and ref locals example.

Ссылочные локальные переменныеRef locals

Ссылочная локальная переменная задает ссылку на значения, возвращаемые с помощью return ref.A ref local variable is used to refer to values returned using return ref. Ссылочная локальная переменная не может инициализироваться как не ссылочное возвращаемое значение.A ref local variable cannot be initialized to a non-ref return value. Другими словами, правая часть инициализации должна быть ссылкой.In other words, the right hand side of the initialization must be a reference. Любые изменения в значении ссылочной локальной переменной отражаются в состоянии объекта, метод которого возвращает значение по ссылке.Any modifications to the value of the ref local are reflected in the state of the object whose method returned the value by reference.

Ссылочная локальная переменная определяется с помощью ключевого слова ref перед объявлением переменной, а также непосредственно перед вызовом метода, который возвращает значение по ссылке.You define a ref local by using the ref keyword before the variable declaration, as well as immediately before the call to the method that returns the value by reference.

Например, следующая инструкция определяет значение ссылочной локальной переменной, которое возвращается методом GetEstimatedValue:For example, the following statement defines a ref local value that is returned by a method named GetEstimatedValue:

ref decimal estValue = ref Building.GetEstimatedValue();

Вы можете таким же образом обратиться к значению по ссылке.You can access a value by reference in the same way. В некоторых случаях обращение к значению по ссылке повышает производительность, поскольку позволяет избежать потенциально затратной операции копирования.In some cases, accessing a value by reference increases performance by avoiding a potentially expensive copy operation. Например, в следующей инструкции показано, как можно определить локальное ссылочное значение, используемое для ссылки на значение.For example, the following statement shows how one can define a ref local value that is used to reference a value.

ref VeryLargeStruct reflocal = ref veryLargeStruct;

Обратите внимание, что в обоих примерах ключевое слово ref необходимо использовать в обоих местах. В противном случае возникает ошибка компилятора CS8172, "Невозможно инициализировать значением переменную по ссылке".Note that in both examples the ref keyword must be used in both places, or the compiler generates error CS8172, "Cannot initialize a by-reference variable with a value."

Начиная с версии C# 7.3, переменная итерации инструкции foreach может иметь вид локальной ссылочной переменной или локальной ссылочной переменной только для чтения.Beginning with C# 7.3, the iteration variable of the foreach statement can be ref local or ref readonly local variable. Дополнительные сведения см. в статье, посвященной инструкции foreach.For more information, see the foreach statement article.

Ссылочные локальные переменные только для чтенияRef readonly locals

Ссылочная локальная переменная только для чтения (ref readonly) позволяет сохранить ссылку на значения, возвращаемые методом или свойством, которые имеют в сигнатуре модификатор ref readonly и используют return ref.A ref readonly local is used to refer to values returned by the method or property that has ref readonly in its signature and uses return ref. Переменная ref readonly сочетает свойства локальной переменной ref и переменной readonly. Она является псевдонимом для хранилища, которому она присвоена, и не может изменять свое значение.A ref readonly variable combines the properties of a ref local variable with a readonly variable: it is an alias to the storage it's assigned to, and it cannot be modified.

Пример использования возвращаемых ссылочных значений и ссылочных локальных переменныхA ref returns and ref locals example

В следующем примере определяется класс Book, содержащий два поля String: Title и Author.The following example defines a Book class that has two String fields, Title and Author. Также определяется класс BookCollection, который включает частный массив объектов Book.It also defines a BookCollection class that includes a private array of Book objects. Отдельные объекты книг возвращаются по ссылке путем вызова метода GetBookByTitle.Individual book objects are returned by reference by calling its GetBookByTitle method.


public class Book
{
    public string Author;
    public string Title;
}

public class BookCollection
{
    private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
    private Book nobook = null;

    public ref Book GetBookByTitle(string title)
    {
        for (int ctr = 0; ctr < books.Length; ctr++)
        {
            if (title == books[ctr].Title)
                return ref books[ctr];
        }
        return ref nobook;
    }

    public void ListBooks()
    {
        foreach (var book in books)
        {
            Console.WriteLine($"{book.Title}, by {book.Author}");
        }
        Console.WriteLine();
    }
}

Если вызывающий объект сохраняет значение, возвращаемое методом GetBookByTitle, в качестве ссылочной локальной переменной, изменения, которые этот объект вносит в возвращаемое значение, отражаются в объекте BookCollection, как показано в следующем примере.When the caller stores the value returned by the GetBookByTitle method as a ref local, changes that the caller makes to the return value are reflected in the BookCollection object, as the following example shows.

var bc = new BookCollection();
bc.ListBooks();

ref var book = ref bc.GetBookByTitle("Call of the Wild, The");
if (book != null)
    book = new Book { Title = "Republic, The", Author = "Plato" };
bc.ListBooks();
// The example displays the following output:
//       Call of the Wild, The, by Jack London
//       Tale of Two Cities, A, by Charles Dickens
//       
//       Republic, The, by Plato
//       Tale of Two Cities, A, by Charles Dickens

Типы ссылочных структурRef struct types

Добавление модификатора ref в объявление struct определяет, что экземпляры этого типа должны размещаться в стеке.Adding the ref modifier to a struct declaration defines that instances of that type must be stack allocated. Другими словами, экземпляры этих типов никогда не создаются из кучи в роли члена другого класса.In other words, instances of these types can never be created on the heap as a member of another class. Главным стимулом для создания этой функции была структура Span<T> и связанные структуры.The primary motivation for this feature was Span<T> and related structures.

Необходимость поддержки типа ref struct как выделенной в стеке переменной вводит ряд правил, применяемых компилятором ко всем типам ref struct.The goal of keeping a ref struct type as a stack-allocated variable introduces several rules that the compiler enforces for all ref struct types.

  • ref struct не поддерживает упаковку.You can't box a ref struct. Тип ref struct невозможно присвоить переменной типа object, dynamic или любому типу интерфейса.You cannot assign a ref struct type to a variable of type object, dynamic, or any interface type.
  • Типы ref struct не могут реализовывать интерфейсы.ref struct types cannot implement interfaces.
  • ref struct невозможно объявить как член класса или обычной структуры.You can't declare a ref struct as a member of a class or a normal struct.
  • Невозможно объявить локальные переменные, которые являются типами ref struct в асинхронных методах.You cannot declare local variables that are ref struct types in async methods. Вы можете объявлять их в синхронных методах, которые возвращают типы Task, Task<TResult> или Task.You can declare them in synchronous methods that return Task, Task<TResult> or Task-like types.
  • Локальные переменные ref struct невозможно объявить в итераторах.You cannot declare ref struct local variables in iterators.
  • Невозможно захватить переменные ref struct в лямбда-выражениях или локальных функциях.You cannot capture ref struct variables in lambda expressions or local functions.

Эти ограничения позволяют избежать случайного использования ref struct, которое может повысить его уровень до управляемой кучи.These restrictions ensure you don't accidentally use a ref struct in a manner that could promote it to the managed heap.

Вы можете сочетать модификаторы и объявить структуру как readonly ref.You can combine modifiers to declare a struct as readonly ref. Объявление readonly ref struct объединяет все преимущества и ограничения объявлений ref struct и readonly struct.A readonly ref struct combines the benefits and restrictions of ref struct and readonly struct declarations.

Спецификация языка C#C# language specification

Дополнительные сведения см. в спецификации языка C#.For more information, see the C# Language Specification. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.The language specification is the definitive source for C# syntax and usage.

См. такжеSee also