checked
y unchecked
n'apparaissent pas au niveau des IL. Ils sont uniquement utilisés dans le code source C# pour indiquer au compilateur s'il doit choisir les instructions IL avec ou sans vérification lorsqu'il remplace la préférence par défaut de la configuration de construction (qui est définie par un drapeau de compilateur).
Bien sûr, il y aura généralement une différence de performance due au fait que des opcodes différents ont été émis pour les opérations arithmétiques (mais pas à cause de l'entrée ou de la sortie du bloc). On s'attend généralement à ce que l'arithmétique vérifiée ait une certaine surcharge par rapport à l'arithmétique non vérifiée correspondante.
En fait, considérez ce programme 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;
}
}
Il s'agit de l'IL générée, avec les optimisations activées et l'arithmétique non vérifiée par défaut :
.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
}
}
Comme vous pouvez le voir, le checked
y unchecked
sont simplement un concept de code source - il n'y a pas d'IL émis lors du passage d'un bloc à l'autre de ce qui était (dans la source) un bloc checked
et un unchecked
contexte. Ce qui change, ce sont les opcodes émis pour les opérations arithmétiques directes (dans ce cas, add
y add.ovf
) qui étaient textuellement inclus dans ces blocs. La spécification couvre les opérations qui sont affectées :
Les opérations suivantes sont affectées par le contexte de contrôle de débordement établi par les opérateurs et les instructions contrôlés et non contrôlés :
- Les opérateurs unaires prédéfinis ++ et -- (§7.6.9 et §7.7.5), lorsque l'opérande est de type intégral.
- L'opérateur unaire prédéfini (§7.7.2), lorsque l'opérande est de type intégral.
- Les opérateurs binaires prédéfinis +, -, * et / (§7.8), lorsque les deux opérandes sont de type intégral.
- Conversions numériques explicites (§6.2.1) d'un type intégral à un autre type intégral, ou d'un flottant ou d'un double à un type intégral.
Et comme vous pouvez le voir, une méthode appelée à partir d'une checked
o unchecked
conservera son corps et ne recevra aucune information sur le contexte à partir duquel il a été appelé. Ceci est également précisé dans les spécifications :
Les opérateurs vérifiés et non vérifiés n'affectent le contexte de vérification du débordement que pour les opérations qui sont textuellement contenues dans les jetons "(" et ")". Les opérateurs n'ont aucun effet sur les membres de la fonction qui sont invoqués à la suite de l'évaluation de l'expression contenue.
Dans l'exemple
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
return checked(Multiply(1000000, 1000000));
}
}
l'utilisation de checked dans F n'affecte pas l'évaluation de x * y dans Multiply, donc x * y est évalué dans le contexte de contrôle de débordement par défaut.
Comme indiqué, l'IL ci-dessus a été généré avec les optimisations du compilateur C# activées. Les mêmes conclusions peuvent être tirées de l'IL qui est émis sans ces optimisations.