const
e readonly
sono simili, ma non sono esattamente gli stessi.
Un const
campo è una costante di compilazione, il che significa che quel valore può essere calcolato in fase di compilazione. Un readonly
campo abilita ulteriori scenari in cui è necessario eseguire un codice durante la costruzione del tipo. Dopo la costruzione, un readonly
campo non può essere modificato.
Ad esempio, i const
membri possono essere utilizzati per definire membri come:
struct Test
{
public const double Pi = 3.14;
public const int Zero = 0;
}
Poiché valori come 3.14 e 0 sono costanti di tempo di compilazione. Tuttavia, considera il caso in cui definisci un tipo e desideri fornirne alcune istanze pre-fab. Ad esempio, potresti voler definire una classe Color e fornire "costanti" per colori comuni come Nero, Bianco, ecc. Non è possibile farlo con i membri const, poiché i lati di destra non sono costanti di compilazione. Si potrebbe fare questo con membri statici regolari:
public class Color
{
public static Color Black = new Color(0, 0, 0);
public static Color White = new Color(255, 255, 255);
public static Color Red = new Color(255, 0, 0);
public static Color Green = new Color(0, 255, 0);
public static Color Blue = new Color(0, 0, 255);
private byte red, green, blue;
public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}
Ma poi non c'è nulla che impedisca a un cliente di Color di confondersi con esso, magari scambiando i valori in bianco e nero. Inutile dire che ciò causerebbe costernazione per altri clienti della classe Color. La funzionalità "sola lettura" risolve questo scenario.
Introducendo semplicemente la readonly
parola chiave nelle dichiarazioni, preserviamo l'inizializzazione flessibile, evitando al contempo che il codice client si rovini.
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
private byte red, green, blue;
public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}
È interessante notare che i membri const sono sempre statici, mentre un membro di sola lettura può essere statico o meno, proprio come un campo normale.
È possibile utilizzare una singola parola chiave per questi due scopi, ma ciò porta a problemi di versione o problemi di prestazioni. Supponiamo per un momento che abbiamo usato una sola parola chiave per questo (const) e uno sviluppatore ha scritto:
public class A
{
public static const C = 0;
}
e un altro sviluppatore ha scritto codice che si basava su A:
public class B
{
static void Main() => Console.WriteLine(A.C);
}
Ora, il codice generato può fare affidamento sul fatto che AC è una costante di compilazione? Vale a dire, l'uso di AC può essere semplicemente sostituito dal valore 0? Se dici "sì" a questo, allora ciò significa che lo sviluppatore di A non può cambiare il modo in cui AC è inizializzato - questo lega le mani dello sviluppatore di A senza permesso.
Se dici "no" a questa domanda, allora manca un'ottimizzazione importante. Forse l'autore di A è positivo che AC sarà sempre zero. L'uso di const e readonly consente allo sviluppatore di A di specificare l'intento. Ciò consente un miglior comportamento del controllo delle versioni e anche prestazioni migliori.
static readonly
: prova a usare una const all'interno di unaIEnumerator
che provocherebbe un irrecuperabileyield
e otterrai un temuto "errore del compilatore interno" . Non ho testato il codice al di fuori di Unity3D, ma confido che si tratti di un bug mono o .NET . È comunque un problema c # .