checked
e unchecked
non vengono visualizzati a livello IL. Vengono utilizzati solo nel codice sorgente C# per indicare al compilatore se selezionare o meno le istruzioni IL di controllo o non di controllo quando si esegue l'override della preferenza predefinita della configurazione di build (impostata tramite un flag del compilatore).
Naturalmente, in genere ci sarà una differenza di prestazioni dovuta al fatto che sono stati emessi diversi codici opzionali per le operazioni aritmetiche (ma non per entrare o uscire dal blocco). In genere si prevede che l'aritmetica verificata abbia un overhead rispetto alla corrispondente aritmetica non controllata.
È un dato di fatto, considerano questo programma C#:
class Program
{
static void Main(string[] args)
{
var a = 1;
var b = 2;
int u1, c1, u2, c2;
Console.Write("unchecked add ");
unchecked
{
u1 = a + b;
}
Console.WriteLine(u1);
Console.Write("checked add ");
checked
{
c1 = a + b;
}
Console.WriteLine(c1);
Console.Write("unchecked call ");
unchecked
{
u2 = Add(a, b);
}
Console.WriteLine(u2);
Console.Write("checked call ");
checked
{
c2 = Add(a, b);
}
Console.WriteLine(c2);
}
static int Add(int a, int b)
{
return a + b;
}
}
Questa è la generata IL, con le ottimizzazioni attivate e con l'aritmetica incontrollato di default:
.class private auto ansi beforefieldinit Checked.Program
extends [mscorlib]System.Object
{
.method private hidebysig static int32 Add (
int32 a,
int32 b
) cil managed
{
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: add
IL_0003: ret
}
.method private hidebysig static void Main (
string[] args
) cil managed
{
.entrypoint
.locals init (
[0] int32 b
)
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: stloc.0
IL_0003: ldstr "unchecked add "
IL_0008: call void [mscorlib]System.Console::Write(string)
IL_000d: dup
IL_000e: ldloc.0
IL_000f: add
IL_0010: call void [mscorlib]System.Console::WriteLine(int32)
IL_0015: ldstr "checked add "
IL_001a: call void [mscorlib]System.Console::Write(string)
IL_001f: dup
IL_0020: ldloc.0
IL_0021: add.ovf
IL_0022: call void [mscorlib]System.Console::WriteLine(int32)
IL_0027: ldstr "unchecked call "
IL_002c: call void [mscorlib]System.Console::Write(string)
IL_0031: dup
IL_0032: ldloc.0
IL_0033: call int32 Checked.Program::Add(int32, int32)
IL_0038: call void [mscorlib]System.Console::WriteLine(int32)
IL_003d: ldstr "checked call "
IL_0042: call void [mscorlib]System.Console::Write(string)
IL_0047: ldloc.0
IL_0048: call int32 Checked.Program::Add(int32, int32)
IL_004d: call void [mscorlib]System.Console::WriteLine(int32)
IL_0052: ret
}
}
Come si può vedere , i blocchi checked
e unchecked
sono semplicemente un concetto di codice sorgente: non vi è alcun IL emesso quando si passa avanti e indietro tra ciò che era (nella sorgente) un checked
e un contesto unchecked
. Ciò che cambia sono gli opcode emessi per le operazioni aritmetiche dirette (in questo caso, add
e add.ovf
) che sono stati racchiusi testualmente in quei blocchi. La specifica comprende che sono influenzati:
Le seguenti operazioni sono interessati dal contesto trabocco controllando stabilito dagli operatori controllati e incontrollati e dichiarazioni:
- il predefinito ++ e - (unari §7.6.9 e §7.7.5), quando l'operando è di tipo integrale.
- L'operatore predefinito - unario (§7.7.2), quando l'operando è di tipo integrale.
- Gli operatori predefiniti +, -, * e/binari (§7.8), quando entrambi gli operandi sono di tipo integrale.
- Conversioni numeriche esplicite (§6.2.1) da un tipo integrale a un altro tipo integrale o da variabile o doppio a un tipo integrale.
E come si può vedere, un metodo chiamato da un blocco checked
o unchecked
manterrà il suo corpo e non riceverà alcuna informazione su ciò che contesto è stato chiamato da. Questo è anche specificato nelle specifiche:
Gli operatori controllati e non controllati interessano solo il contesto di controllo di overflow per quelle operazioni che sono contenute testualmente nei token "(" e ")". Gli operatori non hanno alcun effetto sui membri delle funzioni invocati come risultato della valutazione dell'espressione contenuta.
Nell'esempio
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
return checked(Multiply(1000000, 1000000));
}
}
l'uso controllato in F non pregiudica la valutazione di x * y in Moltiplicare, quindi x * y viene valutato nel contesto controllo troppopieno predefinito.
Come indicato, l'IL sopra riportato è stato generato con le ottimizzazioni del compilatore C# attivate. Le stesse conclusioni possono essere tratte dall'IL che viene emesso senza queste ottimizzazioni.
Dubito che 'checked' e' unschecked' lo facciano come blocchi. Probabilmente dicono semplicemente al compilatore di emettere un codice diverso nelle istruzioni aritmetiche che appaiono direttamente in esse. Ma lasciami ... controllare. –
@ TheodorosChatzigiannakis Sono d'accordo, il blocco 'checked' emette semplicemente le istruzioni' .ovf'. –
https://en.wikipedia.org/wiki/List_of_CIL_instructions Heh, @EldarDordzhiev mi ha battuto su di esso - le versioni '.ovf' delle istruzioni aritmetiche sono emesse - aggiungono un po 'di overhead contro usando" plain " istruzioni aritmetiche. – xxbbcc