checked
und unchecked
Blöcke erscheinen nicht auf der IL-Ebene. Sie werden nur im C# -Quellcode verwendet, um dem Compiler mitzuteilen, ob die überprüfenden oder nicht überprüfenden IL-Anweisungen ausgewählt werden sollen, wenn die Standardpräferenz der Erstellungskonfiguration überschrieben wird (die über ein Compiler-Flag festgelegt wird).
Natürlich wird es einen Leistungsunterschied geben, weil verschiedene Opcodes für die arithmetischen Operationen ausgegeben wurden (aber nicht, weil der Block betreten oder verlassen wurde). Überprüfte Arithmetik wird im Allgemeinen mit einem Overhead verglichen mit entsprechenden ungeprüften Arithmetikdaten erwartet.
als eine Angelegenheit der Tatsache, dieses C# Programm betrachten:
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;
}
}
Dies ist die erzeugte IL, mit Optimierungen auf und mit ungeprüften Arithmetik standardmäßig aktiviert:
.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
}
}
Wie Sie sehen können Die checked
und unchecked
Blöcke sind lediglich ein Quellcode-Konzept - es gibt keine IL, die beim Hin- und Herschalten zwischen dem (in der Quelle) checked
und einem unchecked
Kontext emittiert wird. Was sich ändert, sind die Operationscodes, die für direkte arithmetische Operationen (in diesem Fall add
und add.ovf
) ausgegeben wurden, die in diesen Blöcken textlich eingeschlossen waren. Die Spezifikation umfasst, welche Operationen betroffen sind:
Die folgenden Operationen durch den Überlauf überprüft Zusammenhang mit den geprüften und ungeprüften Operatoren und Anweisungen etabliert betroffen sind:
- Die vordefinierte ++ und - unäre Operatoren (§7.6.9 und §7.7.5), wenn der Operand ein ganzzahliger Typ ist.
- Der vordefinierte - unäre Operator (§7.7.2), wenn der Operand ein ganzzahliger Typ ist.
- Die vordefinierten Operatoren +, -, * und/binary (§7.8), wenn beide Operanden ganzzahlige Typen sind.
- Explizite numerische Konvertierungen (§6.2.1) von einem Integraltyp in einen anderen Integraltyp oder von Float oder Double in einen Integraltyp.
Und wie Sie sehen können, ist ein Verfahren aus einem Block checked
oder unchecked
genannt wird seinen Körper behalten und es wird keine Informationen über nicht erhalten, was Kontext aus aufgerufen wurde. Dies wird auch in der Spezifikation erläutert:
Die aktivierten und nicht aktivierten Operatoren wirken sich nur auf den Überlaufprüfkontext für die Operationen aus, die textlich in den Token "(" und ") enthalten sind. Die Operatoren haben keine Auswirkungen auf Funktionsmember, die als Ergebnis der Auswertung des enthaltenen Ausdrucks aufgerufen werden.
Im Beispiel
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
return checked(Multiply(1000000, 1000000));
}
}
die Verwendung der überprüften in F wirkt sich nicht auf die Auswertung von x * y in Multiplizieren, so x * y in der Standardüberlaufprüfung Kontext ausgewertet wird.
Wie bereits erwähnt, wurde die obige IL mit aktivierten C# -Compiler-Optimierungen generiert. Die gleichen Schlussfolgerungen können aus der IL gezogen werden, die ohne diese Optimierungen ausgegeben wird.
Ich bezweifle, dass "checked" und "unchecked" es als Blöcke in die IL machen. Sie sagen dem Compiler möglicherweise einfach, in arithmetischen Anweisungen, die direkt in ihnen erscheinen, einen anderen Code auszugeben. Aber lass mich ... nachsehen. –
@TheodorosChatzigiannakis Ich stimme zu, der "checked" Block gibt nur die '.ovf' Anweisungen aus. –
https://en.wikipedia.org/wiki/List_of_CIL_instructions Heh, @EldarDordzhiev hat mich dazu gedrängt - die '.ovf' Versionen der arithmetischen Befehle werden ausgegeben - sie fügen ein wenig Overhead hinzu, anstatt die" Ebene "zu verwenden arithmetische Anweisungen. – xxbbcc