Pull to refresh

C#: как «выстрелить себе в ногу»

Reading time 2 min
Views 16K
Рассмотрим, как можно «выстрелить себе в ногу» на C# (и в целом в .NET).
Оказывается, такое можно сделать не только на C++.

Ниже представлен код, «играющийся» с типом bool (System.Boolean) и выводящий на экран 20 строк True или False.
Для первых десяти строк поведение детерминировано только для первой строки, а поведение для 2-10 строк зависит от реализации компилятора.

Код вполне самодокументированный, поэтому добавлю только, что на подобное поведение вполне можно наткнуться при вызове неуправляемых функций через P/Invoke, в случае их некорректного использования (на эту тему будет продолжение).

//#define USE_POINTER_ARITHMETIC_FOR_UNSAFE_MODE

using System;
using System.Globalization;
using System.Runtime.InteropServices;

namespace ConsoleApplication
{
    /// <summary>
    /// Main Program Class
    /// </summary>
    static class Program
    {
        /// <summary>
        /// Converts Byte Code to Boolean Value
        /// </summary>
        /// <param name="code">Byte Code</param>
        /// <param name="unsafeMode">Use Unsafe Way to Convert</param>
        /// <returns>Boolean Value</returns>
        static bool ByteToBool(byte code, bool unsafeMode)
        {
            if (unsafeMode)
            {
#if USE_POINTER_ARITHMETIC_FOR_UNSAFE_MODE
                // This Code Needs The "/unsafe" Compiler Directive:
                unsafe { return *(bool*)&code; }
#else
                // This Code No Needs The "/unsafe" Compiler Directive:
                return (new UnsafeBool() { Code = code }).Value;
#endif
            }

            return code != 0; // Safe Way
        }

        /// <summary>
        /// Itz a "Funny" Bool Joke!
        /// </summary>
        static void BoolJoke(Action<string> output)
        {
            int row = 0;
            Func<string> getNextRow = () => string.Format(NumberFormatInfo.InvariantInfo, "{0:00}: ", ++row);

            for (int i = 0; i < 2; i++)
            {
                bool isUnsafeMode = i == 0;
                output(isUnsafeMode ? "Unsafe Mode" : "Safe Mode");

                bool b1 = true;                           // Internal Code: 1 (Implementation-Dependent, Undocumented)
                bool b2 = ByteToBool(0xFF, isUnsafeMode); // Value is Invalid
                bool b3 = ByteToBool(0xFE, isUnsafeMode); // Value is Invalid

                output(getNextRow() + "b1        : " + b1);
                output(getNextRow() + "b2        : " + b2);
                output(getNextRow() + " b1 ==  b2: " + (b1 == b2));
                output(getNextRow() + "!b1 == !b2: " + (!b1 == !b2));

                output(getNextRow() + "b1 && b2  : " + (b1 && b2));
                output(getNextRow() + "b1 &  b2  : " + (b1 & b2));
                output(getNextRow() + "b1 ^  b2  : " + (b1 ^ b2));

                output(getNextRow() + "b1 && b3  : " + (b1 && b3));
                output(getNextRow() + "b1 &  b3  : " + (b1 & b3));
                output(getNextRow() + "b1 ^  b3  : " + (b1 ^ b3));

                output(null);
            }
        }

        /// <summary>
        /// Application Entry Point
        /// </summary>
        static void Main()
        {
            BoolJoke(Console.WriteLine);
            Console.ReadKey(); // Press Any Key to Exit
        }
    }

    /// <summary>
    /// Unsafe Boolean Structure
    /// </summary>
    /// <remarks>
    /// See https://msdn.microsoft.com/library/c8f5xwh7.aspx
    /// See https://msdn.microsoft.com/library/eahchzkf.aspx
    /// </remarks>
    [StructLayout(LayoutKind.Explicit)]
    struct UnsafeBool
    {
        /// <summary>
        /// Boolean Value
        /// </summary>
        [FieldOffset(0)]
        public bool Value;

        /// <summary>
        /// Boolean Value Internal Code
        /// </summary>
        [FieldOffset(0)]
        public byte Code;
    }
}
Tags:
Hubs:
-6
Comments 7
Comments Comments 7

Articles