BitArrayクラスを使わずに、数値の指定ビット位置(0から始まる)をオン・オフする
BitOperation.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public class BitOperation
{
/// <summary>
/// 指定したビット位置を変換する
/// </summary>
/// <param name="source">反転する値。</param>
/// <param name="index">反転するビット位置。(0~)</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);
}
}
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
}
}
ジェネリックを使うと
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を実装するオブジェクトだからということも)
ジェネリックの方は、intで指定するとOverflowExceptionが発生する場合がある。
Convert.ChangeType
がuint→intなどの縮小変換はcheckedコンテキストで動作するためらしい。
.NET Framework における型変換 (Convertクラス 基本型どうしの変換)
縮小変換は checked コンテキストで実行され、変換が失敗すると OverflowException がスローされます。
//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