C#: Enums e Structs
1. Enums (enum)
Uma enumeração é um tipo de valor usado para definir um conjunto de constantes nomeadas, tornando o código mais legível e seguro em termos de tipos.
(1) Definindo uma enumeração
Use a palavra-chave enum para definir uma enumeração. O tipo subjacente padrão é int, começando em 0 e incrementando-se.
▶ Exemplo
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
class Program
{
static void Main()
{
Season current = Season.Autumn;
System.Console.WriteLine(current);
System.Console.WriteLine((int)current);
}
}
Autumn
2
(2) Tipo subjacente e atribuição explícita
O tipo subjacente de uma enum é, por padrão, int. É possível especificar explicitamente outro tipo inteiro e atribuir valores específicos aos membros.
▶ Exemplo
enum Priority : byte
{
Low = 1,
Medium = 2,
High = 3
}
enum HttpStatusCode
{
OK = 200,
NotFound = 404,
InternalServerError = 500
}
class Program
{
static void Main()
{
Priority p = Priority.High;
System.Console.WriteLine((byte)p);
System.Console.WriteLine((int)HttpStatusCode.NotFound);
}
}
3
404
(3) Conversão de enumerações
As enums e seus tipos subjacentes podem ser explicitamente convertidos uns nos outros.
▶ Exemplo
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
class Program
{
static void Main()
{
Season s = Season.Spring;
int value = (int)s;
System.Console.WriteLine(value);
Season fromInt = (Season)2;
System.Console.WriteLine(fromInt);
}
}
0
Autumn
2. Métodos da classe Enum
A classe System.Enum oferece vários métodos estáticos para operações com enums.
(1) Métodos comuns
| Método | Descrição |
|---|---|
Enum.Parse |
Converte uma string em uma enumeração; lança uma exceção em caso de falha |
Enum.TryParse |
Converte uma string em uma enumeração; retorna false em caso de falha |
Enum.GetName |
Obtém o nome de um valor de enumeração |
Enum.GetValues |
Obtém todos os valores da enumeração |
Enum.IsDefined |
Determina se um valor está definido na enumeração |
▶ Exemplo
enum Season
{
Spring,
Summer,
Autumn,
Winter
}
class Program
{
static void Main()
{
Season parsed = (Season)Enum.Parse(typeof(Season), "Summer");
System.Console.WriteLine(parsed);
bool ok = Enum.TryParse<Season>("Winter", out Season result);
System.Console.WriteLine($"{ok}, {result}");
string name = Enum.GetName(typeof(Season), 2);
System.Console.WriteLine(name);
foreach (Season s in Enum.GetValues(typeof(Season)))
{
System.Console.WriteLine($"{s} = {(int)s}");
}
System.Console.WriteLine(Enum.IsDefined(typeof(Season), 1));
System.Console.WriteLine(Enum.IsDefined(typeof(Season), 99));
}
}
Summer
True, Winter
Autumn
Spring = 0
Summer = 1
Autumn = 2
Winter = 3
True
False
3. Estruturas (struct)
Uma estrutura (struct) é um tipo de valor adequado para encapsular pequenas quantidades de dados. As estruturas são armazenadas na pilha, e a atribuição copia o valor inteiro.
(1) Definindo uma estrutura
Use a palavra-chave struct para definir uma estrutura.
▶ Exemplo
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public override string ToString()
{
return $"({X}, {Y})";
}
}
class Program
{
static void Main()
{
Point p1 = new Point(3, 4);
Point p2 = p1;
p2.X = 10;
System.Console.WriteLine(p1);
System.Console.WriteLine(p2);
}
}
(3, 4)
(10, 4)
4. Estrutura x Classe
As estruturas e as classes apresentam diferenças importantes em termos de semântica e comportamento.
(1) Comparação
| Característica | estrutura | classe |
|---|---|---|
| Categoria de tipo | Tipo de valor | Tipo de referência |
| Local de armazenamento | Pilha | Heap |
| Comportamento da atribuição | Copia o valor | Copia a referência |
| Herança | Não compatível (não pode ser herdado) | Compatível |
| Interfaces | Pode implementar | Pode implementar |
| Destrutor | Nenhum | Sim |
| Valor padrão | Todos os campos com valor zero | nulo |
▶ Exemplo
struct PointStruct
{
public int X;
public int Y;
}
class PointClass
{
public int X;
public int Y;
}
class Program
{
static void Main()
{
PointStruct s1 = new PointStruct();
PointStruct s2 = s1;
s2.X = 5;
System.Console.WriteLine($"struct: s1.X={s1.X}, s2.X={s2.X}");
PointClass c1 = new PointClass();
PointClass c2 = c1;
c2.X = 5;
System.Console.WriteLine($"class: c1.X={c1.X}, c2.X={c2.X}");
}
}
struct: s1.X=0, s2.X=5
class: c1.X=5, c2.X=5
5. Quando usar estruturas
As estruturas nem sempre são melhores do que as classes. Considere usar uma estrutura quando:
- Os dados são pequenos (recomenda-se ≤16 bytes)
- Representa, logicamente, um único valor (por exemplo, coordenadas, cor)
- Tem uma vida útil curta, com criação e destruição frequentes
- Não é necessário haver herança
Exemplos comuns: Point, Color, DateTime, TimeSpan.
6. Estrutura readonly (C# 7.2+)
readonly struct garante que todos os campos sejam imutáveis. O compilador impede qualquer operação de modificação.
▶ Exemplo
readonly struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public double DistanceTo(Point other)
{
int dx = X - other.X;
int dy = Y - other.Y;
return System.Math.Sqrt(dx * dx + dy * dy);
}
}
class Program
{
static void Main()
{
Point p1 = new Point(3, 4);
Point p2 = new Point(0, 0);
System.Console.WriteLine(p1.DistanceTo(p2));
}
}
5
7. Estrutura record (C# 10+)
record struct combina a semântica de valor de uma struct com a igualdade de valores e a sintaxe concisa de um record, sendo adequado para cenários que exigem imutabilidade e membros gerados automaticamente.
▶ Exemplo
public class Point : IEquatable<Point>
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public Point With(int? x = null, int? y = null)
{
return new Point(x ?? this.X, y ?? this.Y);
}
public bool Equals(Point other)
{
return other != null && X == other.X && Y == other.Y;
}
public override bool Equals(object obj) => Equals(obj as Point);
public override int GetHashCode() => System.HashCode.Combine(X, Y);
public override string ToString() => $"Point {{ X = {X}, Y = {Y} }}";
}
class Program
{
static void Main()
{
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.Console.WriteLine(p1 == p2);
System.Console.WriteLine(p1.With(x: 10));
}
}
True
Point { X = 10, Y = 2 }
record struct gera automaticamente as expressões Equals, ToString, with e outros membros, reduzindo o código repetitivo.
❓ Perguntas Frequentes
P: Uma enumeração pode especificar um tipo que não seja inteiro? R: Não, só é possível especificar tipos inteiros (byte, sbyte, short, ushort, int, uint, long, ulong).
P: Uma estrutura pode ter um construtor? R: Sim, ela pode ter construtores parametrizados, mas o construtor sem parâmetros é fornecido automaticamente pelo compilador e não pode ser definido de forma personalizada.
P: Uma struct pode implementar interfaces? R: Sim, as structs podem implementar interfaces. O boxing ocorre quando uma struct é convertida para um tipo de interface (conversão de valor para referência).
P: Os campos de uma estrutura readonly podem usar o set? R: Não, todos os campos de uma estrutura readonly devem ser readonly e só podem ser inicializados por meio do construtor.
P: Qual é a diferença entre enum e const? R: Uma enum é um conjunto de constantes nomeadas com segurança de tipo; const é uma única constante em tempo de compilação, sem capacidade de agrupamento de tipos.
📖 Resumo
- As enums são definidas com
enum; o tipo subjacente padrão éint, com incremento a partir de 0 - O tipo subjacente e os valores dos membros podem ser especificados explicitamente
Enum.Parse/TryParse/GetName/GetValues/IsDefinedfornecer operações com enums- As enums e os tipos subjacentes podem ser explicitamente convertidos uns nos outros
- As estruturas são definidas com
struct, são tipos de valor e são armazenadas na pilha - A atribuição de uma estrutura copia o valor; a atribuição de uma classe copia a referência
- Estruturas não podem ser herdadas, mas podem implementar interfaces
- Cenários adequados: dados pequenos (≤16 bytes), imutáveis, com vida útil curta
readonly struct(C# 7.2+) garante que todos os campos sejam imutáveisrecord struct(C# 10+) combina a semântica de valores com a sintaxe concisa dos registros
📝 Exercícios
- Defina um
enum Direction { North, East, South, West }, escreva um código para percorrer todas as direções e exibir o nome e o valor inteiro - Defina um
struct Rectanglecom largura e altura, forneça um método para calcular a área e verifique se a atribuição copia o valor - Altere o
Rectangledo exercício anterior para umreadonly struct, garantindo que todos os campos sejam somente de leitura e sejam inicializados por meio do construtor - Use
Enum.TryParseeEnum.IsDefinedpara escrever um método seguro de análise de enumerações que receba uma string e retorne o valor da enumeração correspondente ou um valor padrão - Escreva um código para comparar a diferença de comportamento entre
structeclassquando passados como parâmetros de método (passagem por valor versus passagem por referência)