Operadores
Operadores simplificados:
Operador condicional (ternário): forma compacta de if-else. Se a condição for verdadeira, retorna o primeiro valor; caso contrário, o segundo.
condition ? true_value : false_value
Operadores checked e unchecked: O bloco checked ativa a verificação de estouro aritmético, lançando OverflowException se ocorrer. O bloco unchecked desativa essa verificação (comportamento padrão).
int valor = 2147483647;
checked
{
valor++;
}
Console.WriteLine(valor); // overflow exception
int valor = 2147483647;
unchecked
{
valor++;
}
Console.WriteLine(valor); // -2147483648
Operador is: Verifica se um objeto é compatível com um tipo específico (se é do tipo ou deriva dele).
object texto = "Olá";
if (texto is string)
{
Console.WriteLine("texto é uma string");
}
Operador as: Converte explicitamente uma referência; se a conversão falhar, retorna null em vez de lançar exceção.
object obj1 = "C#";
object obj2 = 42;
string str1 = obj1 as string; // "C#"
string str2 = obj2 as string; // null
Operador sizeof: Retorna o tamanho em bytes de um tipo de valor na pilha. Para tipos não primitivos, deve estar em um bloco unsafe.
Console.WriteLine(sizeof(decimal)); // 16
Operador typeof: Obtém o objeto Type de um tipo específico.
Type tipo = typeof(DateTime);
Console.WriteLine(tipo.FullName);
Operadores ? (nullable) e ?? (coalescência nula): Tipos anuláveis permitem representar valores indefinidos. O operador ?? retorna o primeiro operanod se não for nulo; caso contrário, retorna o segundo.
int? a = null;
int? b = a + 10; // null
int? c = a * 3; // null
int? valor = null;
int resultado = valor ?? -1; // resultado = -1
valor = 5;
resultado = valor ?? -1; // resultado = 5
Prioridade de Operadores
A ordem de precedência segue as regras padrão da linguagem. Por exemplo, multiplicação e divisão têm precedência sobre adição e subtração.
Segurança de Tipos e Conversões
Conversão implícita: Ocorre automaticamente quando não há perda de dados.
int valorInt = 100;
long valorLong = valorInt; // implícita
Conversão explícita (cast): Necessária quando pode haver perda de dados ou quando o tipo de destino é "menor".
double valorDouble = 123.45;
int valorInt = (int)valorDouble; // 123
Boxing e Unboxing: Boxing é a conversão de um tipo de valor para object (ou qualquer tipo de interface que implemente). Unboxing é a operação inversa.
int numero = 42;
object obj = numero; // boxing (implícito)
int numeroDeVolta = (int)obj; // unboxing (explícito)
Comparação de Igualdade
ReferenceEquals: Método estático que verifica se duas referências apontam para a mesma instância.
object a = new object();
object b = a;
Console.WriteLine(object.ReferenceEquals(a, b)); // True
Equals virtual: Pode ser sobrescrito para comparar valores.
Equals estático: Trata valores nulos de forma segura.
Operador ==: Por padrão, compara referências em tipos de referência, mas pode ser sobrecarregado para comparar valores.
Sobrecarga de Operadores
É possível definir operadores personalizados para tipos definidos pelo usuário usando a palavra-chave operator. O exemplo a seguir sobrecarrega o operador + para somar dois vetores 2D.
public struct Vetor2D
{
public double X;
public double Y;
public Vetor2D(double x, double y) { X = x; Y = y; }
public static Vetor2D operator +(Vetor2D v1, Vetor2D v2)
{
return new Vetor2D(v1.X + v2.X, v1.Y + v2.Y);
}
public override string ToString() => $"({X}, {Y})";
}
// Uso:
Vetor2D v1 = new Vetor2D(3.0, 4.0);
Vetor2D v2 = new Vetor2D(1.5, 2.5);
Vetor2D v3 = v1 + v2;
Console.WriteLine(v3); // (4.5, 6.5)
Operadores que podem ser sobrecarregados incluem: +, -, *, /, ==, !=, <, >, etc. Alguns operadores como && e || não podem ser sobrecarregados diretamente, mas podem ser simulados através de & e |.
Conversões Definidas pelo Usuário
É possível criar conversões implícitas ou explícitas entre tipos próprios usando implicit operator ou explicit operator. O exemplo abaixo define uma estrutura Temperatura que permite conversão de/para double (Celsius).
public struct Temperatura
{
private double celsius;
public Temperatura(double celsius) { this.celsius = celsius; }
// Conversão implícita de double para Temperatura
public static implicit operator Temperatura(double graus)
{
return new Temperatura(graus);
}
// Conversão explícita de Temperatura para double
public static explicit operator double(Temperatura t)
{
return t.celsius;
}
public override string ToString() => $"{celsius} °C";
}
// Uso:
Temperatura t1 = 25.5; // implícita
double valor = (double)t1; // explícita
Console.WriteLine(t1); // 25,5 °C
Console.WriteLine(valor); // 25,5
Regras importantes:
- Conversões implícitas não devem lançar exceções nem perder dados.
- Uma conversão deve ser definida dentro do tipo de origem ou do tipo de destino.
- Não é possível definir conversões entre tipos que já possuem relação de herança.