0Pricing
C# Academy · Lezione

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 con Equals/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

  1. Overload degli operatori aritmetici
  2. Overload degli operatori di confronto
  3. Conversioni definite dall'utente
  4. Best practice per l'overload degli operatori
← Torna a C# Academy