ref

Domains: C#

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.
  • 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

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. 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.

 Note

Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. 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.

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

An argument that is passed to a ref or in parameter must be initialized before it is passed. This differs from out parameters, whose arguments do not have to be explicitly initialized before they are passed.

Members of a class can't have signatures that differ only by refin, or out. A compiler error occurs if the only difference between two members of a type is that one of them has a refparameter 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) { }
}

However, methods can be overloaded when one method has a refin, 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 other situations that require signature matching, such as hiding or overriding, inref, and out are part of the signature and don't match each other.

Properties are not variables. They are methods, and cannot be passed to ref parameters.

You can't use the refin, and out keywords for the following kinds of methods:

  • Async methods, which you define by using the async modifier.
  • 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. 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. 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.

A reference return value is defined by using the ref keyword:

  • In the method signature. For example, the following method signature indicates that the GetCurrentPrice method returns a Decimal value by reference.
 
public ref decimal GetCurrentPrice()
  • 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.

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. 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

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.

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.

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;

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."

Beginning with C# 7.3, the iteration variable of the foreach statement can be ref local or ref readonly local variable. For more information, see the foreach statement article.

Ref readonly locals

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. 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

The following example defines a Book class that has two String fields, Title and Author. It also defines a BookCollection class that includes a private array of Book objects. 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();
    }
}

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

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. The primary motivation for this feature was Span<T> and related structures.

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.

  • You can't box a ref struct. You cannot assign a ref struct type to a variable of type objectdynamic, or any interface type.
  • ref struct types cannot implement interfaces.
  • You can't declare a ref struct as a member of a class or a normal struct.
  • You cannot declare local variables that are ref struct types in async methods. You can declare them in synchronous methods that return Task, Task<TResult> or Task-like types.
  • You cannot declare ref struct local variables in iterators.
  • You cannot capture ref struct variables in lambda expressions or local functions.

These restrictions ensure you don't accidentally use a ref struct in a manner that could promote it to the managed heap.

You can combine modifiers to declare a struct as readonly ref. A readonly ref struct combines the benefits and restrictions of ref struct and readonly struct declarations.

Similar pages

Page structure
Terms

C#