Boas práticas para sobrecarga de operadores
Mantenha os operadores sobrecarregados intuitivos.
Boas práticas para sobrecarga de operadores é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.
Boas práticas para sobrecarga de operadores
A sobrecarga de operadores é uma ferramenta poderosa. Quando bem usada, torna os tipos de valor intuitivos; quando mal usada, torna o código enigmático. Esta lição aborda princípios que mantêm os operadores sobrecarregados previsíveis.
Sobrecarregue apenas quando o significado for óbvio
Sobrecarregue um operador somente quando seu significado for inequívoco para o seu tipo. + em um vetor significa claramente uma adição componente a componente; + em um Order apenas confundiria os leitores.
using System;
public struct Vector2
{
public int X, Y;
public Vector2(int x, int y) { X = x; Y = y; }
// + means adding components: obvious and expected
public static Vector2 operator +(Vector2 a, Vector2 b)
=> new Vector2(a.X + b.X, a.Y + b.Y);
public override string ToString() => "(" + X + ", " + Y + ")";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Vector2(1, 2) + new Vector2(3, 4));
}
}Preserve as expectativas matemáticas
Mantenha os operadores de acordo com a intuição: a + b deve ser igual a b + a quando a matemática for comutativa, e + não deve subtrair secretamente. Surpresas desse tipo causam erros sutis.
using System;
public struct Money
{
public decimal Amount;
public Money(decimal a) { Amount = a; }
public static Money operator +(Money a, Money b) => new Money(a.Amount + b.Amount);
public override string ToString() => "$" + Amount;
}
public class Program
{
public static void Main()
{
var x = new Money(3);
var y = new Money(5);
Console.WriteLine((x + y).Amount == (y + x).Amount); // commutative
}
}Mantenha a simetria e os pares
A linguagem combina determinados operadores, mas você também deve manter a simetria lógica: se adicionar +, considere -; se adicionar <, forneça também > e as variantes com igualdade. Conjuntos de operadores incompletos frustram os usuários.
using System;
public struct Length
{
public int Mm;
public Length(int mm) { Mm = mm; }
public static Length operator +(Length a, Length b) => new Length(a.Mm + b.Mm);
public static Length operator -(Length a, Length b) => new Length(a.Mm - b.Mm);
public override string ToString() => Mm + "mm";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Length(50) + new Length(20));
Console.WriteLine(new Length(50) - new Length(20));
}
}Não altere os operandos
Os operadores devem ser puros: calcular e retornar um novo valor, deixando os operandos intactos. Alterar um operando dentro de + quebra todas as expectativas do leitor.
using System;
public struct Vector2
{
public int X, Y;
public Vector2(int x, int y) { X = x; Y = y; }
public static Vector2 operator +(Vector2 a, Vector2 b)
=> new Vector2(a.X + b.X, a.Y + b.Y); // returns new, mutates nothing
public override string ToString() => "(" + X + ", " + Y + ")";
}
public class Program
{
public static void Main()
{
var a = new Vector2(1, 1);
var unused = a + new Vector2(9, 9);
Console.WriteLine("a is still " + a);
}
}Mantenha == consistente com Equals
Uma boa prática recorrente: sempre que sobrecarregar ==, substitua Equals e GetHashCode para que todos os caminhos de igualdade concordem. A inconsistência nesse ponto é um dos erros mais comuns em operadores.
using System;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public override bool Equals(object obj) => obj is Point p && p.X == X && p.Y == Y;
public override int GetHashCode() => HashCode.Combine(X, Y);
public static bool operator ==(Point a, Point b) => a.Equals(b);
public static bool operator !=(Point a, Point b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Point(1, 1) == new Point(1, 1));
}
}Forneça alternativas nomeadas
Algumas linguagens e ferramentas de análise não conseguem usar operadores. Oferecer um método com nome explícito, como Add, junto com +, melhora a interoperabilidade e a facilidade de descoberta.
using System;
public struct Vector2
{
public int X, Y;
public Vector2(int x, int y) { X = x; Y = y; }
public Vector2 Add(Vector2 other) => new Vector2(X + other.X, Y + other.Y);
public static Vector2 operator +(Vector2 a, Vector2 b) => a.Add(b);
public override string ToString() => "(" + X + ", " + Y + ")";
}
public class Program
{
public static void Main()
{
var v = new Vector2(1, 2);
Console.WriteLine(v.Add(new Vector2(3, 4)));
Console.WriteLine(v + new Vector2(3, 4));
}
}Prefira operadores em tipos semelhantes a valores
Os operadores parecem naturais em tipos imutáveis semelhantes a valores (estruturas ou classes semelhantes a registros). Para tipos mutáveis e fortemente baseados em identidade, métodos nomeados geralmente comunicam melhor a intenção.
using System;
public readonly struct Rational
{
public readonly int Num, Den;
public Rational(int n, int d) { Num = n; Den = d; }
public static Rational operator *(Rational a, Rational b)
=> new Rational(a.Num * b.Num, a.Den * b.Den);
public override string ToString() => Num + "/" + Den;
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Rational(1, 2) * new Rational(2, 3));
}
}Documente os casos extremos
Seja explícito sobre como os operadores lidam com valores especiais: divisão por zero, operandos nulos para classes ou estouro. Um tratamento previsível evita que os chamadores sejam pegos de surpresa.
using System;
public struct SafeDiv
{
public int Value;
public SafeDiv(int v) { Value = v; }
// Documented behavior: dividing by zero yields zero
public static SafeDiv operator /(SafeDiv a, SafeDiv b)
=> new SafeDiv(b.Value == 0 ? 0 : a.Value / b.Value);
public override string ToString() => Value.ToString();
}
public class Program
{
public static void Main()
{
Console.WriteLine(new SafeDiv(10) / new SafeDiv(0));
Console.WriteLine(new SafeDiv(10) / new SafeDiv(2));
}
}Um conjunto disciplinado de operadores
Um tipo numérico bem elaborado combina alguns operadores intuitivos, igualdade consistente e imutabilidade. O resultado se comporta como um número integrado e é agradável de usar.
using System;
public readonly struct Vec
{
public readonly double X, Y;
public Vec(double x, double y) { X = x; Y = y; }
public static Vec operator +(Vec a, Vec b) => new Vec(a.X + b.X, a.Y + b.Y);
public static Vec operator -(Vec a, Vec b) => new Vec(a.X - b.X, a.Y - b.Y);
public static Vec operator *(Vec a, double k) => new Vec(a.X * k, a.Y * k);
public override string ToString() => "(" + X + ", " + Y + ")";
}
public class Program
{
public static void Main()
{
Console.WriteLine((new Vec(1, 2) + new Vec(3, 4)) * 2);
}
}Experimente você mesmo
Reúna as práticas: um tipo de valor imutável, operadores intuitivos, uma alternativa nomeada e igualdade consistente.
using System;
public readonly struct Meters : IEquatable<Meters>
{
public readonly double Value;
public Meters(double v) { Value = v; }
public Meters Add(Meters other) => new Meters(Value + other.Value);
public static Meters operator +(Meters a, Meters b) => a.Add(b);
public bool Equals(Meters other) => Value == other.Value;
public override bool Equals(object obj) => obj is Meters m && Equals(m);
public override int GetHashCode() => Value.GetHashCode();
public static bool operator ==(Meters a, Meters b) => a.Equals(b);
public static bool operator !=(Meters a, Meters b) => !a.Equals(b);
public override string ToString() => Value + "m";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Meters(3) + new Meters(4));
Console.WriteLine(new Meters(7) == new Meters(7));
}
}Verificação rápida
Aplique as boas práticas para operadores.
Recapitulação
Uma boa sobrecarga de operadores é disciplinada e previsível.
- Sobrecarregue apenas quando o significado for óbvio.
- Preserve as expectativas matemáticas e a simetria.
- Mantenha a pureza: retorne novos valores e nunca altere os operandos.
- Mantenha
==consistente comEquals/GetHashCode. - Ofereça alternativas nomeadas e prefira tipos semelhantes a valores.
Perguntas Frequentes
A aula “Boas práticas para sobrecarga de operadores” é grátis?
Sim — o texto completo de “Boas práticas para sobrecarga de operadores” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.
O que vou aprender em “Boas práticas para sobrecarga de operadores”?
Mantenha os operadores sobrecarregados intuitivos. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar C# Academy?
Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Boas práticas para sobrecarga de operadores”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de C# Academy?
Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Sobrecarga de operadores aritméticos
- Sobrecarga de operadores de comparação
- Conversões definidas pelo usuário
- Boas práticas para sobrecarga de operadores