Best practice per l'overload degli operatori
Mantenga intuitivi gli operatori sottoposti a overload.
Best practice per l'overload degli operatori è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.
Buone pratiche per il sovraccarico degli operatori
Il sovraccarico degli operatori è uno strumento potente. Usato bene, rende intuitivi i tipi valore; usato male, rende il codice oscuro. Questa lezione illustra i principi che mantengono prevedibili gli operatori sovraccaricati.
Sovraccaricare solo quando il significato è ovvio
Sovraccarichi un operatore solo quando il suo significato è inequivocabile per il Suo tipo. + su un vettore indica chiaramente un'addizione componente per componente; + su un Order non farebbe che confondere chi legge.
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));
}
}Rispettare le aspettative matematiche
Mantenga gli operatori coerenti con l'intuizione: a + b dovrebbe essere uguale a b + a quando la matematica è commutativa, e + non dovrebbe sottrarre di nascosto. Le sorprese in questo ambito causano bug difficili da individuare.
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
}
}Mantenere simmetria e corrispondenza
Il linguaggio abbina alcuni operatori, ma dovrebbe mantenere anche la simmetria logica: se aggiunge +, consideri -; se aggiunge <, fornisca anche > e le varianti con uguaglianza. Insiemi di operatori incompleti frustrano gli utenti.
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));
}
}Non modificare gli operandi
Gli operatori dovrebbero essere puri: calcolare e restituire un nuovo valore, lasciando invariati gli operandi. Modificare un operando all'interno di + contraddice ogni aspettativa di chi legge il codice.
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);
}
}Mantenere == coerente con Equals
Una buona pratica ricorrente: ogni volta che sovraccarica ==, esegua l'override di Equals e GetHashCode, così tutti i percorsi di uguaglianza concordano. L'incoerenza in questo ambito è uno dei bug degli operatori più comuni.
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));
}
}Fornire alternative con nome
Alcuni linguaggi e strumenti di analisi non possono usare gli operatori. Offrire un metodo con nome come Add accanto a + migliora l'interoperabilità e la reperibilità.
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));
}
}Preferire operatori per i tipi basati sul valore
Gli operatori risultano naturali sui tipi immutabili e basati sul valore, come struct o classi simili ai record. Per i tipi modificabili e con una forte identità, i metodi con nome comunicano generalmente meglio l'intento.
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));
}
}Documentare i casi limite
Specifichi esplicitamente come gli operatori gestiscono i valori speciali: la divisione per zero, gli operandi null per le classi o l'overflow. Una gestione prevedibile evita di cogliere di sorpresa chi chiama il codice.
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));
}
}Un insieme di operatori ben progettato
Un tipo numerico ben progettato combina pochi operatori intuitivi, un'uguaglianza coerente e l'immutabilità. Il risultato si comporta come un numero predefinito ed è piacevole da usare.
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);
}
}Provi Lei
Metta insieme queste pratiche: un tipo valore immutabile, operatori intuitivi, un'alternativa con nome e un'uguaglianza coerente.
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 rapida
Applichi le buone pratiche per gli operatori.
Riepilogo
Un buon sovraccarico degli operatori è disciplinato e prevedibile.
- Sovraccarichi gli operatori solo quando il significato è ovvio.
- Rispetti le aspettative matematiche e la simmetria.
- Mantenga la purezza: restituisca nuovi valori e non modifichi mai gli operandi.
- Mantenga
==coerente conEquals/GetHashCode. - Offra alternative con nome e preferisca i tipi basati sul valore.
Domande Frequenti
La lezione «Best practice per l'overload degli operatori» è gratuita?
Sì — il testo completo di «Best practice per l'overload degli operatori» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.
Cosa imparerò in «Best practice per l'overload degli operatori»?
Mantenga intuitivi gli operatori sottoposti a overload. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare C# Academy?
Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Best practice per l'overload degli operatori»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione C# Academy?
Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Overload degli operatori aritmetici
- Overload degli operatori di confronto
- Conversioni definite dall'utente
- Best practice per l'overload degli operatori