ref (Referência de C#)

A ref palavra-chave indica que uma variável é uma referência ou um alias para outro objeto. Ele é usado em cinco contextos diferentes:

  • Em uma assinatura de método e em uma chamada de método, para passar um argumento a um método por referência. Para obter mais informações, veja Passar um argumento por referência.
  • Em uma assinatura de método para retornar um valor para o chamador por referência. Para obter mais informações, consulte Reference return values (Valores retornados de referência).
  • Em um corpo de membro, para indicar que um valor retornado por referência é armazenado localmente como uma referência que o chamador pretende modificar. Ou para indicar que uma variável local acessa outro valor por referência. Para obter mais informações, veja Locais de referência.
  • Em uma declaração struct para declarar um ref struct ou um readonly ref struct. Para saber mais, confira o artigo ref struct.
  • Em uma ref struct declaração, para declarar que um campo é uma referência. Consulte o artigo do ref campo .

Passando um argumento por referência

Quando usado na lista de parâmetros do método, a palavra-chave ref indica que um argumento é passado por referência, não por valor. A palavra-chave ref torna o parâmetro formal um alias para o argumento, que deve ser uma variável. Em outras palavras, qualquer operação no parâmetro é feita no argumento.

Por exemplo, suponha que o chamador passe uma expressão de variável local ou uma expressão de acesso de elemento de matriz. Em seguida, o método chamado pode substituir o objeto ao qual o parâmetro ref se refere. Nesse caso, a variável local do chamador ou do elemento da matriz referem-se ao novo objeto quando o método retorna.

Observação

Não confunda o conceito de passar por referência com o conceito de tipos de referência. Os dois conceitos não são iguais. Um parâmetro de método pode ser modificado por ref, independentemente de ele ser um tipo de valor ou um tipo de referência. Não há nenhuma conversão boxing de um tipo de valor quando ele é passado por referência.

Para usar um parâmetro ref, a definição do método e o método de chamada devem usar explicitamente a palavra-chave ref, como mostrado no exemplo a seguir. (Exceto que o método de chamada pode omitir ref ao fazer uma chamada COM.)

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

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

Um argumento que é passado para um ref ou in parâmetro deve ser inicializado antes de ser passado. Esse requisito difere dos parâmetros externos , cujos argumentos não precisam ser inicializados explicitamente antes de serem passados.

Os membros de uma classe não podem ter assinaturas que se diferem somente por ref, in ou out. Ocorrerá um erro de compilador se a única diferença entre os dois membros de um tipo for que um deles tem um parâmetro ref e o outro tem um parâmetro out ou in. O código a seguir, por exemplo, não é compilado.

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) { }
}

No entanto, os métodos podem ser sobrecarregados quando um método tem um parâmetro ref, in ou out e o outro tem um parâmetro que é passado por valor, conforme mostrado no exemplo a seguir.

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

Em outras situações que exigem correspondência de assinatura, como ocultar ou substituir, in, ref e out fazem parte da assinatura e não são correspondentes.

As propriedades não são variáveis. Eles são métodos e não podem ser passados para ref parâmetros.

Não é possível usar as palavras-chave ref, in e out para os seguintes tipos de métodos:

  • Métodos assíncronos, que você define usando o modificador async.
  • Métodos de iterador, que incluem uma instrução yield return ou yield break.

Os métodos de extensão também têm restrições sobre o uso dessas palavras-chave:

  • A out palavra-chave não pode ser usada no primeiro argumento de um método de extensão.
  • A ref palavra-chave não pode ser usada no primeiro argumento de um método de extensão quando o argumento não é um struct ou um tipo genérico não restrito a ser um struct.
  • A in palavra-chave não pode ser usada a menos que o primeiro argumento seja um struct. A in palavra-chave não pode ser usada em nenhum tipo genérico, mesmo quando restrita para ser um struct.

Passando um argumento por referência: um exemplo

Os exemplos anteriores passam tipos de valor por referência. Você também pode usar a palavra-chave ref para passar tipos de referência por referência. Passar um tipo de referência por referência permite que o método chamado substitua o objeto ao qual se refere o parâmetro de referência no chamador. O local de armazenamento do objeto é passado para o método como o valor do parâmetro de referência. Se você alterar o valor no local de armazenamento do parâmetro (para apontar para um novo objeto), irá alterar também o local de armazenamento ao qual se refere o chamador. O exemplo a seguir passa uma instância de um tipo de referência como um parâmetro ref.

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 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;
}

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);
}

// This method displays the following output:
// Original values in Main.  Name: Fasteners, ID: 54321
// Back in Main.  Name: Stapler, ID: 12345

Para obter mais informações sobre como passar tipos de referência por valor e por referência, consulte Passando parâmetros de tipo de referência.

Valores retornados por referência

Valores retornados por referência (ou ref returns) são valores que um método retorna por referência para o chamador. Ou seja, o chamador pode modificar o valor retornado por um método e essa alteração será refletida no estado do objeto no método chamado.

Um valor retornado por referência é definido usando a palavra-chave ref:

  • Na assinatura do método. Por exemplo, a assinatura de método a seguir indica que o método GetCurrentPrice retorna um valor Decimal por referência.
public ref decimal GetCurrentPrice()
  • Entre o token return e a variável retornada em uma instrução return no método. Por exemplo:
return ref DecimalArray[0];

Para que o chamador modifique o estado do objeto, o valor retornado de referência deve ser armazenado em uma variável que é definida explicitamente como um ref local.

Aqui está um exemplo de retorno de ref mais completo, mostrando a assinatura do método e o corpo do método.

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

O método chamado também poderá declarar o valor retornado como ref readonly para retornar o valor por referência e, em seguida, impor que o código de chamada não possa modificar o valor retornado. O método de chamada pode evitar a cópia retornada com um valor ao armazenar o valor em um local ref readonly variável.

Para obter um exemplo, consulte Um exemplo de ref returns e ref locals.

Ref locals

Uma variável de ref local é usada para fazer referência a valores retornados usando return ref. Uma variável local ref não pode ser inicializada para um valor de retorno não ref. Em outras palavras, o lado direito da inicialização deve ser uma referência. Todas as modificações ao valor do ref local são refletidas no estado do objeto cujo método retornou o valor por referência.

Você define um ref local usando a palavra-chave ref em dois lugares:

  • Antes da declaração da variável.
  • Imediatamente antes da chamada para o método que retorna o valor por referência.

Por exemplo, a instrução a seguir define um valor de ref local que é retornado por um método chamado GetEstimatedValue:

ref decimal estValue = ref Building.GetEstimatedValue();

Você pode acessar um valor por referência da mesma maneira. Em alguns casos, acessar um valor por referência aumenta o desempenho, evitando uma operação de cópia potencialmente dispendiosa. Por exemplo, a instrução a seguir mostra como definir uma variável local ref que é usada para fazer referência a um valor.

ref VeryLargeStruct reflocal = ref veryLargeStruct;

Em ambos os exemplos, a ref palavra-chave deve ser usada em ambos os lugares ou o compilador gera o erro CS8172, "Não é possível inicializar uma variável por referência com um valor".

A variável de iteração da foreach instrução pode ser uma variável local ref local ou ref readonly. Para saber mais, confira o artigo Instrução foreach. Você pode reatribuir uma variável local ref local ou ref readonly com o operador de atribuição ref.

Locais somente leitura de referência

O local ref readonly é usado para fazer referência a valores retornados pelo método ou propriedade que tem ref readonly na sua assinatura e usa return ref. Uma ref readonly variável combina as propriedades de uma ref variável local com uma readonly variável: é um alias para o armazenamento ao qual foi atribuída e não pode ser modificada.

Um exemplo de ref returns e ref locals

O exemplo a seguir define uma classe Book que tem dois campos String, Title e Author. Ele também define uma classe BookCollection que inclui uma matriz privada de objetos Book. Objetos de catálogo individuais são retornados por referência chamando o respectivo método GetBookByTitle.


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();
    }
}

Quando o chamador armazena o valor retornado pelo método GetBookByTitle como um ref local, as alterações que o chamador faz ao valor retornado são refletidas no objeto BookCollection, conforme mostra o exemplo a seguir.

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

campos ref

Em ref struct tipos, você pode declarar campos que são ref campos. ref os campos são válidos somente em ref struct tipos para garantir que a referência não sobreviva ao objeto ao qual se refere. Esse recurso habilita tipos como System.Span<T>:

public readonly ref struct Span<T>
{
    internal readonly ref T _reference;
    private readonly int _length;

    // Omitted for brevity...
}

O Span<T> tipo armazena uma referência por meio da qual acessa os elementos consecutivos. Uma referência permite que o Span<T> objeto evite fazer cópias do armazenamento ao qual se refere.

Especificação da linguagem C#

Para obter mais informações, consulte a Especificação da linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Confira também