整数ビット演算


[BitArrayクラス](https://msdn.microsoft.com/ja-jp/library/system.collections.bitarray(v=vs.110).aspx)を使わずに、数値の指定ビット位置（０から始まる）をオン・オフする

```csharp:BitOperation.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class BitOperation
{
    /// <summary>
    ///  指定したビット位置を変換する
    /// </summary>
    /// <param name="source">反転する値。</param>
    /// <param name="index">反転するビット位置。（０～）</param>
    /// <param name="bitSize">変換する値のビット数。</param>
    /// <param name="bitOn">ビットオンの場合、True。ビットオフの場合、False。</param>
    /// <returns>ビット操作を行った値。</returns>
    /// <remarks>
    /// UInt64 のサイズを超える値は変換できない
    /// </remarks>
    private static UInt64 Flip(UInt64 source, byte index, byte bitSize, bool bitOn)
    {
        byte size = (byte)(bitSize - 1);
        if (index > size)
        {
            throw new ArgumentOutOfRangeException("index", "ビット位置は、0～" + size + "の範囲で指定してください。");
        }

        UInt64 basedata = (UInt64)1 << index;
        UInt64 rc = source;

        if (bitOn == true)
        {
            // BIT ON
            rc = (UInt64)(rc | basedata);
        }
        else
        {
            // BIT OFF
            UInt64 reverse = (UInt64)(~basedata);
            rc = (UInt64)(rc & reverse);
        }

        return rc;
    }

    public static byte Flip(byte source, byte index, bool bitOn)
    {
        return (byte)Flip((UInt64)source, index, 8, bitOn);
    }

    public static Int32 Flip(Int32 source, byte index, bool bitOn)
    {
        return (Int32)Flip((UInt64)source, index, 32, bitOn);
    }

    public static UInt32 Flip(UInt32 source, byte index, bool bitOn)
    {
        return (UInt32)Flip((UInt64)source, index, 32, bitOn);
    }

    public static Int64 Flip(Int64 source, byte index, bool bitOn)
    {
        return (Int64)Flip((UInt64)source, index, 64, bitOn);
    }

    public static UInt64 Flip(UInt64 source, byte index, bool bitOn)
    {
        return Flip(source, index, 64, bitOn);
    }
}
```

```csharp:Main.cs
using System.IO;
using System;

class Program
{
    static void Main()
    {
        byte b = 0;
        b = BitOperation.Flip(b, 0, true);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x1
        b = BitOperation.Flip(b, 7, true);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x81
        b = BitOperation.Flip(b, 7, false);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x1
        b = BitOperation.Flip(b, 0, false);
        Console.WriteLine(string.Format("0x{0:X}", b)); // 0x0
        UInt64 ui = 0;
        ui = BitOperation.Flip(ui, 0, true);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x1
        ui = BitOperation.Flip(ui, 63, true);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x8000000000000001
        ui = BitOperation.Flip(ui, 63, false);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x1
        ui = BitOperation.Flip(ui, 0, false);
        Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x0
    }
}
```

ジェネリックを使うと

```csharp
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class BitOperation
{
    private static UInt64 Flip(UInt64 source, byte index, byte bitSize, bool bitOn)
    {
        byte size = (byte)(bitSize - 1);
        if (index > size)
        {
            throw new ArgumentOutOfRangeException("index", "ビット位置は、0～" + size + "の範囲で指定してください。");
        }

        UInt64 basedata = (UInt64)1 << index;
        UInt64 rc = source;

        if (bitOn == true)
        {
            // BIT ON
            rc = (UInt64)(rc | basedata);
        }
        else
        {
            // BIT OFF
            UInt64 reverse = (UInt64)(~basedata);
            rc = (UInt64)(rc & reverse);
        }

        return rc;
    }

    public static T Flip<T>(T source, byte index, bool bitOn)
     where T : struct, IConvertible
    {
        Type t = typeof(T);
        UInt64 src = (UInt64)Convert.ChangeType(source, typeof(UInt64));
        UInt64 to = Flip(src, index, (byte)(Marshal.SizeOf(t) * 8), bitOn);
        return (T)Convert.ChangeType(to, t);
    }
}
```
struct, IConvertible指定はsourceを数値型とみなす（気休め程度）
（`Convert.ChangeType`の第1引数はIConvertibleを実装するオブジェクトだからということも）
> [Convert.ChangeTypeメソッド](https://msdn.microsoft.com/ja-jp/library/dtb69x08(v=vs.110).aspx)

ジェネリックの方は、intで指定するとOverflowExceptionが発生する場合がある。
`Convert.ChangeType`がuint→intなどの縮小変換は[checked](https://msdn.microsoft.com/ja-jp/library/khy08726.aspx)コンテキストで動作するためらしい。

> [.NET Framework における型変換](https://msdn.microsoft.com/ja-jp/library/98bbex99(v=vs.110).aspx) （Convertクラス 基本型どうしの変換）
>> 縮小変換は checked コンテキストで実行され、変換が失敗すると OverflowException がスローされます。

```csharp
//OverflowExceptionが発生。
//int i = BitOperation.Flip<int>(0, 31, true);
//uintで呼び出してintにキャストすれば回避できる
int ii = (int)BitOperation.Flip<uint>(0, 31, true);
Console.WriteLine(string.Format("0x{0:X}", ii)); // 0x80000000

uint ui = BitOperation.Flip<uint>(0, 31, true);
Console.WriteLine(string.Format("0x{0:X}", ui)); // 0x80000000
```