Tipos de valor – (referência de C#)

Tipos de valor e tipos de referência são as duas categorias principais de tipos C#. Uma variável de um tipo de valor contém uma instância do tipo. Isso é diferente de uma variável de um tipo de referência, que contém uma referência a uma instância do tipo. Por padrão, na atribuição, ao passar um argumento para um método e retornar um resultado de método os valores de variável são copiados. No caso de variáveis de tipo de valor, as instâncias de tipo correspondentes são copiadas. O exemplo a seguir demonstra esse comportamento:

using System;

public struct MutablePoint
{
    public int X;
    public int Y;

    public MutablePoint(int x, int y) => (X, Y) = (x, y);

    public override string ToString() => $"({X}, {Y})";
}

public class Program
{
    public static void Main()
    {
        var p1 = new MutablePoint(1, 2);
        var p2 = p1;
        p2.Y = 200;
        Console.WriteLine($"{nameof(p1)} after {nameof(p2)} is modified: {p1}");
        Console.WriteLine($"{nameof(p2)}: {p2}");

        MutateAndDisplay(p2);
        Console.WriteLine($"{nameof(p2)} after passing to a method: {p2}");
    }

    private static void MutateAndDisplay(MutablePoint p)
    {
        p.X = 100;
        Console.WriteLine($"Point mutated in a method: {p}");
    }
}
// Expected output:
// p1 after p2 is modified: (1, 2)
// p2: (1, 200)
// Point mutated in a method: (100, 200)
// p2 after passing to a method: (1, 200)

Como mostra o exemplo anterior, as operações em uma variável de tipo de valor afetam apenas essa instância do tipo de valor armazenada na variável.

Se um tipo de valor contiver um membro de dados de um tipo de referência, somente a referência à instância do tipo de referência será copiada quando uma instância de tipo de valor for copiada. A instância de cópia e de tipo de valor original tem acesso à mesma instância de tipo de referência. O exemplo a seguir demonstra esse comportamento:

using System;
using System.Collections.Generic;

public struct TaggedInteger
{
    public int Number;
    private List<string> tags;

    public TaggedInteger(int n)
    {
        Number = n;
        tags = new List<string>();
    }

    public void AddTag(string tag) => tags.Add(tag);

    public override string ToString() => $"{Number} [{string.Join(", ", tags)}]";
}

public class Program
{
    public static void Main()
    {
        var n1 = new TaggedInteger(0);
        n1.AddTag("A");
        Console.WriteLine(n1);  // output: 0 [A]

        var n2 = n1;
        n2.Number = 7;
        n2.AddTag("B");

        Console.WriteLine(n1);  // output: 0 [A, B]
        Console.WriteLine(n2);  // output: 7 [A, B]
    }
}

Observação

Para tornar seu código menos propenso a erros e mais robusto, defina e use tipos de valor imutáveis. Este artigo usa tipos de valor mutáveis somente para fins de demonstração.

Modalidades de tipos de valor e restrições de tipo

Um tipo de valor pode ser de uma das duas seguintes modalidades:

  • um tipo de estrutura, que encapsula dados e funcionalidade relacionada
  • um tipo de enumeração, que é definido por um conjunto de constantes nomeadas e representa uma escolha ou uma combinação de opções

Um tipo de valor anulável T? representa todos os valores do tipo de valor subjacente T e um valor nulo adicional. Você não pode atribuir null a uma variável de um tipo de valor, a menos que seja um tipo de valor anulável.

Você pode usar a struct restrição para especificar que um parâmetro de tipo é um tipo de valor não anulável. Os tipos de estrutura e enumeração atendem à restrição struct. Você pode usar System.Enum em uma restrição de classe base (conhecida como restrição de enumeração) para especificar que um parâmetro de tipo é um tipo de enumeração.

Tipos de valor internos

O C# fornece os seguintes tipos de valor internos, também conhecidos como tipos simples:

Todos os tipos simples são tipos de estrutura, e diferem de outros tipos de estrutura por permitirem determinadas operações adicionais:

  • Você pode usar literais para fornecer um valor de um tipo simples.
    Por exemplo, 'A' é um literal do tipo char, 2001 é um literal do tipo int e 12.34m é um literal do tipo decimal.

  • Você pode declarar constantes dos tipos simples com a palavra-chave const.
    Por exemplo, você pode definir const decimal = 12.34m.
    Não é possível ter constantes de outros tipos de estrutura.

  • As expressões constantes, cujos operandos são todos constantes de tipo simples, são avaliadas em tempo de compilação.

Uma tupla de valor é um tipo de valor, mas não um tipo simples.

Especificação da linguagem C#

Para obter mais informações, confira as seguintes seções da especificação da linguagem C#:

Confira também