2012年2月16日木曜日

C 系のビット操作と C# の enum

最近のプログラマはビット操作をやらないそうだ。
というより、毛の抜けた SE も今は滅多にやらなくなった。
そういう時代なのだろう。

だが、ビットをフラグとして扱うビット判定は今でもちょくちょく使う。
代表的な使用例が enum だ。以下 C# の例。

[FlagsAttribute]
enum AruFlags
{
    None   = 0x00, // 0000 0000
    One     = 0x01, // 0000 0001
    Two    = 0x02, // 0000 0010
    Three = 0x04, // 0000 0100
    Four  = 0x08, // 0000 1000
    All     = 0x0F // 0000 1111
};

enum は便利なので良く使う。
この enum にフラグ属性を持たせる場合は、上記のように宣言の前部に [FlagsAttribute] を加える。
それだけではダメで、中身の各 ID に対してビットフラグを定義する。
「うん?」
と思った人はプログラマだろう。
そう、何も [FlagsAttribute] を付けなくても、enum は中身は int なのだから、各 ID に対して 2 の y 乗を定義すれば良いんじゃない?
と思うだろう。実際、毛の抜けた SE は以前はそうしていた。
論より証拠、上記のコードから [FlagsAttribute] を取り去って、以下のようにビット判定しても何も問題は起きない。

AruFlags sonota = AruFlgas.One | AruFlags.Three;

if ((sonota & AruFlags.One) != 0) Console.WriteLine("One が入ってます");
if ((sonota & AruFlags.Two) != 0) Console.WriteLine("Two が入ってます");
if ((sonota & AruFlags.Three) != 0) Console.WriteLine("Three が入ってます");
if ((sonota & AruFlags.Four) != 0) Console.WriteLine("Four が入ってます");

/* 結果はもちろん以下のようになる
One が入ってます
Three が入ってます
*/

では何故、[FlagsAttribute] を加えるかというと、
MSDN リファレンスでは、
「列挙体をビット フィールド、つまりフラグのセットとして扱えることを示します。」
と要約していて、下部の解説で長々と説明しているが、あたかも、これを付けないとフラグ判定できませんよ、とも読める。
が、要するに、作り手(プログラマ)の礼儀作法を教育しているように思われる。
これは何も [FlagsAttribute] に限ったことではなく、最近の(以前から?) Microsoft の方針のようだ。
リファレンスの下部の解説部分でもそういう「ビットフィールドの作成方法」的な部分が大半を占める。

ということは、

「ビットフィールドとは何か」、
「ビットフィールドはどういう状況で使うのか」、
「ビットフィールドを作成・使用する際の注意点は何か」

という、昔は先輩のプログラマに教わったような初歩的な事柄が、

System.FlagsAttribute

という、enum でしか使わない特異で何だか怪しいものの解説ページに一緒に載っているのである。
もとい、大半を占めているのである。
異様な感じに包まれたのは、毛の抜けた SE だけだろうか。。。

しかし、違いはもちろんだが、ある。
FlagsAttribute のページではなく、列挙型のリファレンスにもあるように、
結果を文字列として表示するような場合、
例えば、

MessageBox.Show(sonota.ToString());

この結果が違ってくる。
[FlagsAttribute] 属性を付けると、「One, Three」と表示されるのに対して、
[FlagsAttribute] 属性を付けないと、「5」と表示される。


また、この [FlagsAttribute] 属性について、こんなコードを見つけた。(まんまではない)

[FlagsAttribute]
enum FileFlags
{
    None = 0,
    Read = 1,
    Write = 1 << 1,
    Create = 1 << 2,
    Delete = 1 << 3,
    All = 1 | 1 << 1 | 1 << 2 | 1 << 3
};

ビットフィールドを定義する際に、直に値を定義するのではなく、左シフト演算子を使って桁を上げていって、2 の y 乗を実現している。
懐かしくて、何だか心地よい気分になったもんだが、毛がフサフサなヒヨコにはどうか。
むしろ初心者は、2 のべき乗の値そのものを覚えておくべきだ。(せめて 1024 までは) というのが毛の抜けた SE の教育方針だ。



この enum 便利でよく使うが、あくまでも静的定義なので、VCL の 集合体 Set<> なんかみたいに、enum を組み込んで、Contains みたいなメソッドが使えないかなと思ってあれやこれや調べてたら、無い(笑)。
結局、Enum のクラスメソッドを使って実現するしか手はなさそう。
そして、作った関数がこれ。ID を文字列として検索して値を出力するのと、値を受け取って ID を文字列として返すもの。

public bool Search_Enum<TEnum>(string str, out int num)
{
    num = 0;
    try
    {
      var etype = typeof(TEnum);
      if (!etype.IsEnum) return false;
      if (Enum.GetUnderlyingType(etype) != typeof(int)) return false;
      foreach (int val in Enum.GetValues(etype))
      {
        if (str == Enum.GetName(etype, val))
        {
          num = val;
          return true;
        }
      }
      return false;
    }
    catch (Exception)
    {
      return false;
    }
}

public bool Get_Enum_Value<TEnum>(int num, out string str)
{
    str = "";
    try
    {
      var etype = typeof(TEnum);
      if (!etype.IsEnum) return false;
      if (Enum.GetUnderlyingType(etype) != typeof(int)) return false;
      if (!Enum.IsDefined(etype, num)) return false;
      str = Enum.GetName(etype, num);
      return true;
    }
    catch (Exception)
    {
     return false;
    }
}

う~ん、もっとスマートな方法は無いのかぇ?
しかし、これを作ったのにはもうひとつ理由があって、特に2つ目の関数がその目的なのだが、
enum の .ToString() って遅くない?
この関数つかっている部分と、.ToString() 使ってる部分とで、明らかに速度が違う。。。
何だかなぁ。。。

2012年2月15日水曜日

C# の var

C#3.0から使用できるようになった var ついて



MSDN リファレンス使用例あるのでまんまのコード使わせてもらう


例1

string[] words = { "apple", "strawberry", "grape", "peach", "banana" };
var wordQuery = from word in words
          where word[0] == 'g'
          select word;

foreach (string s in wordQuery)
{
    Console.WriteLine(s);
}



例2:
var custQuery = from cust in customers
          where cust.City == "Phoenix"
          select new { cust.Name, cust.Phone };

foreach (var item in custQuery)
{
    Console.WriteLine("Name={0}, Phone={1}", item.Name, item.Phone);
}





リファレンスにもあるように、例1は string 型しかあり得ないので、var を使う必要は無いが、もちろん使ってもよい。
ところが、例2は型が分からない、というより曖昧にしておいて、コンパイラに判断してもらおうというもの。
当然 foreach で要素を取得する場合の item 変数も明示的に var 宣言しないとダメ。


var は、JavaScript ではご存じローカル変数の一般的な宣言方法なので、C# の var とは全くの別物と理解できるが、言うほど別物でも無かったりするから厄介だ。
※注 Java は厳密な型指定が必要なので JavaScript とは違う。


というのも、今まで Script 系しかやってなくて、C# がおもしろそうだから手を出してみようなんていう輩が、「な~んだ、var あるじゃん」とか言いつつ、なんでもかんでもローカル変数に var 宣言する毛がフサフサなヒヨコが出てくる可能性があるんじゃなかろうか?
もしくは、Java (JavaScript ではない) しかやってなくて、「おっ!?これってあの var かい?」とか言いつつ、(同上)


構造型プログラミング言語である「C」のやり過ぎで毛が抜けてしまった SE としては、頭から湯気が出るような感じなのだ。


C# の var の最大の(?)特徴は、
例えば、int などの単純型(実は System.Int32 のエイリアスだったりする。(だから、new int() などという構文が存在する。これはコンストラクタを呼び出している))以外の、ちょっと型名が複雑になりがちなものを省略できるところにある。
おそらく、そういう意味もあって、例1を掲載しているんだと思う。
リファレンスにもあるように、例1は、IEnumerable<string> という型が決定している。
これが var だけで済むのだからありがたい。と、いうもの。


もちろん、例2での使い方が一般的で、LINQ クエリ操作などで真価を発揮するんだろうが、毛の抜けた SE としては、
「左辺が簡単になる」
というのが1番大きい。


Java も最近では色々と試しているようだが、何せほら、あのクラスの量(継承・多態)と何でもかんでもクラスっていうのは半ば辟易するし、コード補完機能が無い(またはレスポンスが悪い)エディタでプログラミングする気にならないし、何だかよく分からないし。。。


要するに、益々持って C# の方が優位性が高くなるなぁ と思う。
じきに、Android アプリが C# で普通に開発できるようになる はず。