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
checkedyuncheckedchecked:
- 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
floatodoublea una integral o enumtype.