c# - que - ¿Por qué System.Decimal ignora el contexto marcado/no marcado?
por que se habla en masculino (3)
Acabo de tropezar con un sistema. Una vez más la rareza decimal y busco una explicación.
Al emitir un valor de tipo System.Decimal
a algún otro tipo (es decir, System.Int32
), la palabra clave checked
y la opción del compilador -checked
parecen ignorarse.
He creado la siguiente prueba para demostrar la situación:
public class UnitTest
{
[Fact]
public void TestChecked()
{
int max = int.MaxValue;
// Expected if compiled without the -checked compiler option or with -checked-
Assert.Equal(int.MinValue, (int)(1L + max));
// Unexpected
// this would fail
//Assert.Equal(int.MinValue, (int)(1M + max));
// this succeeds
Assert.Throws<OverflowException>(() => { int i = (int)(1M + max); });
// Expected independent of the -checked compiler option as we explicitly set the context
Assert.Equal(int.MinValue, unchecked((int)(1L + max)));
// Unexpected
// this would fail
//Assert.Equal(int.MinValue, unchecked((int)(1M + max)));
// this succeeds
Assert.Throws<OverflowException>(() => { int i = unchecked((int)(1M + max)); });
// Expected independent of the -checked compiler option as we explicitly set the context
Assert.Throws<OverflowException>(() => { int i = checked((int)(1L + max)); });
// Expected independent of the -checked compiler option as we explicitly set the context
Assert.Throws<OverflowException>(() => { int i = checked((int)(1M + max)); });
}
}
Toda mi unidad de investigación ahora no condujo a una explicación adecuada para este fenómeno o incluso a cierta información errónea afirmando que debería funcionar . Mi investigación ya incluía la especificación de C #
¿Hay alguien por ahí que pueda arrojar algo de luz sobre esto?
El CLR ofrece instrucciones de IL para operaciones aritméticas simples como add
( add
), sub
(restar), mul
(multiplicación), div
(división).
Por ejemplo, tomemos una instrucción de add
, que suma dos valores juntos. La instrucción de add
no realiza ninguna comprobación de desbordamiento, pero hay una instrucción llamada add.ovf
, que también agrega dos valores, pero arrojará una OverflowException
si se produce un desbordamiento.
Por lo tanto, cuando esté utilizando el operador checked
, la instrucción o el conmutador del compilador, usará la versión de "comprobación de desbordamiento" de la instrucción de add
( add.ovf
).
Recuerda que esto solo funciona para un "Tipos primitivos" .
Pero con los decimal
las cosas son poco diferentes. CLR no considera el tipo decimal
como un tipo primitivo (aunque sí lo tienen los lenguajes de programación como c # o visual basic), lo que significa que CLR no tiene instrucciones IL que saben cómo manipular decimal
valor decimal
. Si busca un tipo decimal
en .NET Framework SDK Documantation o en el código fuente en ReferenceSources , notará que hay métodos llamados Add, Subtract, Multiply, Divide, etc..
y métodos de sobrecarga de operadores para +, -, *, /, etc
Cuando compila un código que usa decimal
, el compilador generará un código para llamar a miembros decimal
para realizar la operación real. Además, como no hay instrucciones de IL para manipular valores decimal
, los modificadores / sentencias / compiladores marcados / no marcados no tienen efecto. Las operaciones con valores decimal
siempre emitirán una OverflowException
si la operación no se puede realizar de manera segura .
El contexto checked
relaciona con el IL emitido desde su código: básicamente cambia el código de operación utilizado para esas operaciones matemáticas de la versión no verificada a la versión marcada. No puede hacer eso para el decimal
porque el decimal
no es un primitivo , y no tiene códigos de operación directos: todas las operaciones aritméticas están struct MyType
en operadores personalizados, exactamente como lo serían si agregaras tu propio struct MyType
y operadores agregados para eso. Entonces: todo dependerá de si los operadores personalizados definidos por decimal
eligen detectar y lanzar OverflowException
o no, en ese código . Lo que no controlas, y no puedes influir en tu construcción.
Es el tipo decimal
que proporciona las conversiones de decimal
<===> int
. Cuando vuelva a su código, donde la palabra clave checked
podría tener un efecto, ya es un int
o se ha lanzado una excepción.
Lamentablemente, el soporte del operador personalizado de C # no se extiende a permitirle agregar implementaciones de operador marcadas / no marcadas por separado.
Especificación de C # (sección 12.7.14 Los operadores marcados y no verificados) contiene una lista de operadores afectados y declaraciones. Los operadores en su prueba no están en la lista:
Las siguientes operaciones se ven afectadas por el contexto de verificación de desbordamiento establecido por los operadores y declaraciones
checked
yunchecked
checked
:
- Los operadores predefinidos de
++
y--
(§12.7.10 y §12.8.6), cuando el operando es de una integral o enumtype.- El operador predefinido
-
unario (§12.8.3), cuando el operando es de un tipo integral.- Los operadores predefinidos
+
,-
,*
y/
binarios (§12.9), cuando ambos operandos son integrales o enumtypes.- Conversiones numéricas explícitas (§11.3.2) de una integral o enumtype a otra integral o enumtype, o de
float
odouble
a una integral o enumtype.