前提
以下の環境で確認してます
- .Net Framework 4.6.2
- Visual Studio 2015
現象
PenオブジェクトのColorが黒か否かによって、処理を分岐するコードを書いたけどうまく動かない。
Brushes.Blackから生成したpenオブジェクトなのに、下のコードを実行してもBの処理に行ってしまう。。
Pen pen = new Pen(Brushes.Black);
if (pen.Color == Color.Black)
{
// Aの処理
}
else
{
// Bの処理
}
原因
Colorクラス内の operator ==の処理で、
Color.value, state,knownColor,name全てが一致していないとtrueが返らないが、
今回の場合全てが一致しないため。
実はColor.Black == Color.FromArgb(255, 0, 0, 0)の戻り値もFalseになる。
比較を行う際には、回避策の方法でチェックするのが王道なのかも。
ARGBがあっていればTrueで値が返ってもいいと思うんだけど。。
回避法
とりあえず黒だったらA, そうでなければBの分岐に行けばよかったので、Color.ToArgbメソッドでARGBの値に直してチェックしてみてる。
戻り値がintなので、結局上位ビットが切り捨てられて比較が成功してるぽい。
Pen pen = new Pen(Brushes.Black);
if (pen.Color.ToArgb() == Color.Black.ToArgb())
{
// Aの処理
}
else
{
// Bの処理
}
蛇足
Color.Valueの値を確認していると、
Color.Black.Valueは0xffffffffff000000で
Pen.Color.Valueは0x00000000FF000000。
上位32bitの差は何なの?
両方とも元のARGB値はint 0xFF000000だけど、
.Netのソースを追っていくと
Colorの方はint → longにキャスト
Penの方はintの値をlongにキャストして、0xffffffffの論理積をとってる。
Penの処理の理由はわからないけど、戻り値が0x00000000FF000000になるのはまあわかる。
Colorの方はなんで上位ビットに1が立つのか?を見ていくと、符号拡張なるものが原因みたい。
変換元の型が変換先の型より小さい場合は、変換先の型と同じサイズになるように、変換前の値に対して符号拡張またはゼロ拡張が行われます。
変換元の型が符号付きの場合は符号拡張が使われて、符号なしの場合はゼロ拡張が使われます。その後、結果は変換先の型の値として扱われます。
0xFF000000は最上位ビットが1なので符号はマイナス。
なのでint→longへの数値変換時に上位ビットが1で埋められている。
なんで上位ビットを1で埋めるかと言ったら、絶対値を求めるために補数を計算するときに上位桁が0じゃないとおかしくなるから。