C# (dotnetcore) .netの内部実装を覗いてbit演算子の使い方を学んでみよう
前提
c#のboolは1Byte
サイズを確認する場合、sizeof演算子を使って確認できる
Console.WriteLine("boolのサイズ:" + sizeof(Boolean));
実際、boolのサイズは、プラットフォームに依存するらしい。
(ほぼほぼ1Byteでしょうが、1Byteと明記されているオフィシャルは見つかりませんでした・・・)
c#のintは4byte
下記のロジックで確認すると、c#ではint型は4Byte。
Console.WriteLine("intのサイズ:" + sizeof(int));
bitの使い所
保存するデータを極力小さくしたい場合に利用する という選択肢が考えられます。組込系とか大量のデータを保存する場合とか。
1億件の情報を保存する
32項目のフラグをboolで保存すると、32Byte使いますが、intを利用すると、4Byteですみます。
以下の表は、32項目のフラグをboolとintで保存した場合の総容量の違いです。
型 | バイト数 | 最低必要なバイト数 |
---|---|---|
bool | 32Byte | 32,000,000,000 |
int | 4Byte | 400,000,000 |
boolを保存した場合、3Gbyteを超える容量です。
intだと400MByteで済みますね。
ゲームなどはとんでもない量のデータを扱うと思いますので、bit演算が多様されると想像できます。
.netのリファレンス 実装(referencesource)を覗いても、結構使われたりすることが分かります。
.netのリファレンス 実装(referencesource)を覗く
さて本題です。覗いてみます。覗いてみるって少しエロチックな言葉ですね。 鶴の恩返しのようです。が、安心してください。 覗いてもどこかへ行きません。多分(汗)
.netのリファレンス ソースとは
.netのリファレンス ソースとは.netの内部のコードって意味ですね。
元々、c#という言語仕様は国際的な標準化団体で公開されていたのですが、
プログラム自体の中身はブラックボックスだったんですが、
今では公開されているので、windowsフォームを利用したシステム開発でちょくちょく覗いていました。
.netのリファレンス ソースは、Microsoftがどのようにプログラミングしているかを見ることができるため、「学び」にとても役立ちます。
ページの最後にリンクを貼り付けておきます。
Controlクラスでbit演算子を使っているところを覗いてみる
Windowsフォームで必ず使う、Controlクラスを覗いてみる。
フラグを格納する変数の定義
private int state; // See STATE_ constants above
フラグの定義
internal const int STATE_CREATED = 0x00000001; internal const int STATE_VISIBLE = 0x00000002; internal const int STATE_ENABLED = 0x00000004; internal const int STATE_TABSTOP = 0x00000008; internal const int STATE_RECREATE = 0x00000010; internal const int STATE_MODAL = 0x00000020; internal const int STATE_ALLOWDROP = 0x00000040; internal const int STATE_DROPTARGET = 0x00000080; internal const int STATE_NOZORDER = 0x00000100; internal const int STATE_LAYOUTDEFERRED = 0x00000200; internal const int STATE_USEWAITCURSOR = 0x00000400; internal const int STATE_DISPOSED = 0x00000800; // 省略
フラグを確認する様子
フラグを判定する場合、&(AND:論理積)を利用している
public bool Created { get { return(state & STATE_CREATED) != 0; } }
フラグを立てている様子
フラグを立てる場合、|(OR:論理和)を使う。
state |= STATE_CREATED;
フラグをおろしている様子
フラグを下ろす場合、&(AND:論理積)と~(NOT:否定)を使う。
state &= (~STATE_CREATED);
サンプルプログラム
using System; namespace bit { class Program { enum Flgs { None = 0b0000, flg1 = 0b0001, flg2 = 0b0010, flg3 = 0b0100, flg4 = 0b1000 } static void Main(string[] args) { Console.ReadKey(); Flgs flgs = new Flgs(); Console.WriteLine("Flgsのサイズ" + sizeof(Flgs)); //フラグを全て下ろす flgs = Flgs.None; //フラグの状態をチェック CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1)); //flg1のフラグを立てる flgs = flgs | Flgs.flg1; //フラグの状態をチェック CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1)); //flg1のフラグを下ろす flgs = flgs & ~Flgs.flg1; //フラグの状態をチェック CheckFlg(flgs, Flgs.flg1, nameof(Flgs.flg1)); Console.ReadKey(); } /// <summary> /// フラグをチェックする関数 /// </summary> /// <param name="flgs">フラグを設定した変数</param> /// <param name="flgs2">チェックしたいフラグを設定</param> /// <param name="flgName">フラグの名称を設定</param> static void CheckFlg(Flgs flgs, Flgs flgs2, string flgName) { if ((flgs & flgs2) != 0) { Console.WriteLine(flgName + "が立っている"); } else { Console.WriteLine(flgName + "が降りている"); } } } }