「ハッカーのたのしみ」はかなりの良書。いまさらFlagsAttributeのレシピ、リターンズ。.NET FrameworkにBitCountくらい標準であってもいいのにね
以前、FlagsAttributeとビット演算のちょっとしたレシピという記事を書きました。
ご覧頂くとわかるように、とてもダサい実装になっています。記事を掲載してすぐに知人からツッコミがありました。
ツッコミがあったときにすぐに続編記事を書いて訂正しようと思っていたのですが、すっかり忘れていました。
最近でも、いまだに「FlagsAttribute」を検索ワードとして、こちらにたどり着く方も多いようなので、
このままダサい実装を晒し続けて、そのまま参考にされるのはとても心が痛みます。
なので、ダサくない実装をF#、C#、VB.NETの3つの言語で掲載しておこうと思います。
とある知人からの指摘
ブログのFlagsAttributeの記事みたけど、たしかにアレはださすぎるw
BitCountやりたいなら、常識的に考えてビット演算で。Javaの実装とかモロだから参考にするとよいよ。
あと、ビット演算関係では、「ハッカーのたのしみ」って本にいろいろ面白いこと載ってるから読んどいた方がいい。
というような感じでした。そうかー、Javaには標準であるのね。
その後、早速Javaの実装を見たり、「ハッカーのたのしみ」という本を読みました。*1
ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか
- 作者: ジュニア,ヘンリー・S.ウォーレン,Jr.,Henry S. Warren,滝沢徹,玉井浩,鈴木貢,赤池英夫,葛毅,藤波順久
- 出版社/メーカー: エスアイビーアクセス
- 発売日: 2004/09
- メディア: 単行本
- 購入: 35人 クリック: 732回
- この商品を含むブログ (127件) を見る
JavaのbitCountの実装はこんな感じになっている
Javaの Integer.bitCount( i ) という
intの1のビットの数を数えるメソッドの実装は、こんな感じになっているようです。
public static int bitCount(int i) { i = i - ((i >>> 1) & 0x55555555); i = (i & 0x33333333) + ((i >>> 2) & 0x33333333); i = (i + (i >>> 4)) & 0x0f0f0f0f; i = i + (i >>> 8); i = i + (i >>> 16); return i & 0x3f; }
このような実装で、結果的に1のビットの立つ数を計算することができる、と。
一見意味不明のプログラムに見えますが、コードと向きあってちゃんと読めば、それほど難しいことはしていません。
これはいわゆる分割統治を用いたアルゴリズムで、まず2ビットごとに分割した16のブロックに
元々あった2つのビットの和をセットし、次に隣接する2つのブロックの和を計算し、
その結果をまた、4ビットごとに分割された8ブロックにそれぞれ結果をセットする。
といった操作を繰り返すことで、結果的に1のビットの数を数えています。
また、ブロックごとの和が隣接するブロックに影響を与えないのであればビット演算andは不要となることも
上記のコードは示しているというわけです。んー、なるほど。確かに無駄の少ない良質な実装ですね。
と、理屈はわかったのでのですが、
こういう発想は、ビット演算とは無縁に近いプログラミングライフを送っている自分には、
とても思い浮かばなかった。へっぽこプログラマであることを、再確認しました。
ググってみたところ、.NETでこの手の話を書いているひとは、なぜかいらっしゃらないようでした。
ということで、.NETにおけるBitCountの実装サンプルおよび、
FlagsAttributeのレシピを、F#、C#、VB.NETで書きましたので置いておきます。
F#による実装
namespace Library1 [<System.Runtime.CompilerServices.ExtensionAttribute>] module public EnumFlagsExtentions = open System open System.Runtime.CompilerServices ///intについてビットが立っている数を取得します。 [<Extension>] let BitCount (self : int) = self |> fun x -> x - ((x >>> 1) &&& 0x55555555) |> fun x -> ((x >>> 2) &&& 0x33333333) + (x &&& 0x33333333) |> fun x -> (x >>> 4) + x &&& 0x0f0f0f0f |> fun x -> (x >>> 8) + x |> fun x -> (x >>> 16) + x &&& 0x3f ///FlagsAttribute属性が付加されたenumについて、ビットが立っている数を取得します。 [<Extension>] let FlagsCount<'T when 'T: enum<int>> (self : 'T) = if not(self.GetType().IsEnum) then raise (new ArgumentException("enumじゃなきゃだめ")) if not(Attribute.IsDefined(self.GetType(),typeof<FlagsAttribute>)) then raise (new ArgumentException("FlagsAttrebute属性ついてなきゃだめ")) self |> Convert.ToInt32 |> BitCount ///FlagsAttribute属性が付加されたenumについて、ビットが立っているenumの要素を取得します。 [<Extension>] let GetFlags<'T when 'T: enum<int>> (self : 'T) = if not(self.GetType().IsEnum) then raise (new ArgumentException("enumじゃなきゃだめ")) if not(Attribute.IsDefined(self.GetType(),typeof<FlagsAttribute>)) then raise (new ArgumentException("FlagsAttrebute属性ついてなきゃだめ")) let target = self |> Convert.ToInt32 seq{for i in 0..31 do let bit x i = let x = target >>> i let x = x &&& - x x let x = bit target i if (x = 0b1) then yield x <<< i :> obj :?> 'T }
F#では、ジェネリックの制約にenum型を指定することができます。素晴らしいです。
ですので、通常F#で利用する分には、「self.GetType().IsEnum」によるチェックをする必要はありませんが、
C#やVB.NETでは、enum型によるジェネリックの制約は利用できないので、
C#やVB.NETから利用されることも考慮するのであれば、このように実装しておくことが望ましいでしょう。
また、Extension属性を付加することで、C#やVB.NETから利用したときに、ちゃんと拡張メソッドとして機能します。
お試し
namespace ConsoleApplication1 open Library1 module Test = open System open Library1.EnumFlagsExtentions [<Flags()>] type EDocks = | None = 0b00000 | Top = 0b00001 | Left = 0b00010 | Right = 0b00100 | Bottom = 0b01000 | All = 0b01111 let enum0 = EDocks.Non let enum1 = EDocks.Top let enum2 = EDocks.Top ||| EDocks.Left let enum3 = EDocks.Top ||| EDocks.Left ||| EDocks.Right let enum4 = EDocks.Top ||| EDocks.Left ||| EDocks.Right ||| EDocks.Bottom let enum5 = EDocks.Non ||| EDocks.Left ||| EDocks.Left ||| EDocks.Non let _ = Console.WriteLine(enum0 |> FlagsCount) Console.WriteLine(enum1 |> FlagsCount) Console.WriteLine(enum2 |> FlagsCount) Console.WriteLine(enum3 |> FlagsCount) Console.WriteLine(enum4 |> FlagsCount) Console.WriteLine(enum5 |> FlagsCount) enum2 |> GetFlags |> Seq.iter Console.WriteLine Console.WriteLine () enum4 |> GetFlags |> Seq.iter Console.WriteLine (-90000000) |> BitCount |> Console.WriteLine Console.ReadKey()
F#では、FlagsAttributeを付加したenum型を記述する際、
「0b00100」のようにビットで記述することができます。これは直感的に書けてとても便利ですね。
実行結果
0 1 2 3 4 1 Top Left Top Left Right Bottom 15
C#による実装
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ClassLibrary1 { public static class EnumFlagsExtentions { /// <summary> /// intについてビットが立っている数を取得します。 /// </summary> /// <param name="self"></param> /// <returns></returns> public static int BitCount(this int self) { var x = self - ((self >> 1) & 0x55555555); x = ((x >> 2) & 0x33333333) + (x & 0x33333333); x = (x >> 4) + x & 0x0f0f0f0f; x += x >> 8; return (x >> 16) + x & 0xff; } /// <summary> /// FlagsAttribute属性が付加されたenumについて、ビットが立っている数を取得します。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="self"></param> /// <returns></returns> public static int FlagsCount<T>(this T self) where T : struct, IConvertible { if (!self.GetType().IsEnum) throw new ArgumentException("enumじゃなきゃだめ"); if (!Attribute.IsDefined(self.GetType(), typeof(FlagsAttribute))) throw new ArgumentException("FlagsAttrebute属性ついてなきゃだめ"); return BitCount(Convert.ToInt32(self)); } /// <summary> /// FlagsAttribute属性が付加されたenumについて、ビットが立っているenumの要素を取得します。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="self"></param> /// <returns></returns> public static IEnumerable<T> GetFlags<T>(this T self) where T : struct, IConvertible { if (!self.GetType().IsEnum) throw new ArgumentException("enumじゃなきゃだめ"); if (!Attribute.IsDefined(self.GetType(), typeof(FlagsAttribute))) throw new ArgumentException("FlagsAttrebute属性ついてなきゃだめ"); var target = Convert.ToInt32(self); for (int i = 0; i < 32; i++) { var x = target >> i; x &= -x; if (x == 0x0001) yield return (T)Enum.ToObject(typeof(T), x << i); } } } }
VB.NETによる実装
Imports System.Runtime.CompilerServices Public Module EnumFlagsExtentions ''' <summary> ''' Integerについてビットが立っている数を取得します。 ''' </summary> ''' <param name="self"></param> ''' <returns></returns> ''' <remarks></remarks> <Extension()> _ Public Function BitCount(ByVal self As Integer) As Integer Dim x As Integer = self - ((self >> 1) And &H55555555) x = ((x >> 2) And &H33333333) + (x And &H33333333) x = (x >> 4) + x And &HF0F0F0F x += x >> 8 Return (x >> 16) + x And &HFF End Function ''' <summary> ''' FlagsAttribute属性が付加されたenumについて、ビットが立っている数を取得します。 ''' </summary> ''' <typeparam name="T"></typeparam> ''' <param name="self"></param> ''' <returns></returns> ''' <remarks></remarks> <Extension()> _ Public Function FlagsCount(Of T As Structure)(ByVal self As T) If Not self.GetType().IsEnum Then Throw New ArgumentException("enumじゃなきゃだめ") End If If Not Attribute.IsDefined(self.GetType(), GetType(FlagsAttribute)) Then Throw New ArgumentException("FlagsAttrebute属性ついてなきゃだめ") End If Return BitCount(Convert.ToInt32(self)) End Function ''' <summary> ''' FlagsAttribute属性が付加されたenumについて、ビットが立っているenumの要素を取得します。 ''' </summary> ''' <typeparam name="T"></typeparam> ''' <param name="self"></param> ''' <returns></returns> ''' <remarks></remarks> <Extension()> _ Public Function GetFlags(Of T As Structure)(ByVal self As T) As IEnumerable(Of T) If Not self.GetType().IsEnum Then Throw New ArgumentException("enumじゃなきゃだめ") End If If Not Attribute.IsDefined(self.GetType(), GetType(FlagsAttribute)) Then Throw New ArgumentException("FlagsAttrebute属性ついてなきゃだめ") End If Dim target As Integer = Convert.ToInt32(self) Dim result As New List(Of T)() For i As Integer = 0 To 31 Dim x As Integer = target >> i x = x And -x If x = &H1 Then result.Add(DirectCast([Enum].ToObject(GetType(T), x << i), T)) End If Next Return result End Function End Module
.NET4が使えるのならば、C#やVB.NETではCode Contractsが利用できるので、「契約」を用いて
事前条件でenum型であるかどうか、FlagsAttribute属性が付加されているかどうかについて
判定することができればより良い実装にできるのではないかと検討してみたのですが、残念ながらできないようです。
このあたりのメタ的要素の判定についても「契約」で表現でるようになってくれると、
Code Contractsの利用価値が更に高まると思うのですが。これについては今後に期待したいところです。
最後に
というわけで、ダサい実装をやっと訂正できました。複数のbool(Boolean)値を一度に扱うような場合、FlagsAttributeを付加したenumは利用価値が高いことも多いです。
なので、このようなレシピは是非手持ちのクラスライブラリに忍ばせておきたいところです。
余裕があれば、他にJavaで標準でサポートされているInteger.Reverseや
Integer.HighestOneBit、Integer.LowestOneBitなどのメソッドについても実装しておくと嬉しいかもしれません。
それはそうと、C#やVB.NETでもF#と同様に、ジェネリック制約にenum型が使えるようにしてもらいたいものです。
*1:私はなぜか、「ハッカーのたしなみ」と空目します。
とある契約の備忘目録。契約による設計(Design by Contract)で信頼性の高いソフトウェアを構築しよう。
「より堅牢で正確性の高いソフトウェアを作りたいぜ!」と願う.NETデベロッパーお待ちかねの、
契約による設計(DbC)をサポートするCode Contractsが.NET Framework4より利用できるようになります。
C#をベースとして契約による設計をサポートする「Spec#」を利用するという方法もありますが、
学習負担を軽減するためにと、マイクロソフトは言語を意識しなくても開発者が利用できるように、
Code Contractsとして.NET Frameworkで契約をサポートしてくれました。
これは、オブジェクト指向および、オブジェクト指向プログラミングが大好きな.NET開発者にとって、とても良い知らせです。
わたしも待ち望んでいたうちのひとりです。ありがとうマイクロソフト!!という気持ちでいっぱいです。
VisualStudio2010が4月12日(米国)にローンチされることが決まったことですし、
契約による設計の概念および、Code Contractsについて、すこし予習復習しておきたいと思います。
契約による設計はソフトウェアの安全性を高めるもっとも効果的な技法のひとつ
契約による設計(Design by Contract)とは、もともとEiffelというオブジェクト指向言語の設計者であるバートランド・メイヤー大先生が提唱し、同言語で組み込み実装されている機能、または概念である*1。
契約による設計は、正しいソフトウェアおよびプログラムとは、要求されたこと以上のこともそれ以下のことも行わない。という考えのもと、
あらかじめプログラムが満たすべき仕様を、条件としてプログラムコード内に直接盛り込みます。
そうすることで、その義務と利益を明確にしてオブジェクトの仕様から曖昧さを排除し、ソフトウェアの安全性を高める技法です。
オブジェクトのインタフェースを厳密に定義して、オブジェクトとそれを使用するオブジェクトが
それぞれ守るべきことを記述することから、「契約」というメタファが当てられています。
契約による設計では、ある操作について満たされるべき条件(事前条件)、操作を行った後が満たすべき環境を表す条件(事後条件)、
クラスのインスタンスについて常に満たされるべき条件(不変表明)、という3種類の契約を用いる。
この3種類の契約によって仕様ををコードに直接記述してプログラム実行時にそれらの条件を満たしているかを常にチェックすることで、
パラメータや戻り値などの妥当性の保証について、呼び出し側と操作側とでの責任の所在を明確にすることができる。
契約を違反した場合、通常、何らかの例外が投げられる。 なので、この手法では問題を引き起こすコードをすぐに発見することができ、
それらに対して速やかに対処することができる。契約による設計は、契約違反なコード、
つまり仕様を満たさないようなコードを排除するシステマチックな戦略であり、
ソフトウェアの安全性を高めるもっとも効果的な技法のひとつであると言えます。
Code Contractsで契約の静的チェックを利用するには
Visutal Studio2010 Beta2で、契約の静的チェックを行うには、
以下より、CodeContractsをダウンロードし、インストールしてください。
Visutal Strudio2010のアドオンされて、Code Contractsが利用できるようになります。
プロジェクトのプロパティで、静的チェックを行うようチェックボックをチェックすれば、
契約について静的チェックを利用することができるようになります。
DevLabs - Code Contracts
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
事前条件と事後条件
事前条件は、ある操作について、最低限の実行可能条件を定めたものです。ルーチンを呼び出す側を束縛するもので、呼び出しても良い状態を定義します。
Code Contractsでは、Contract.Requiresなどを使います。
(Code Contractsでの事前条件には3つの形態があります。詳しくはMSDNライブラリを参照)
事後条件は、操作を行った後に、環境がどのようになっているべきかを定めたものです。
呼び出されるルーチン側を束縛し、制御を呼び出し側に返すときに保証しなければならない状態を定義します。
Code Contractsでは、Contract.Ensuresなどを使います。
(Code Contractsでの事後条件には2つの形態があります。詳しくはMSDNライブラリを参照)
string Hoge(string x) { Contract.Requires(x != null); Contract.Ensures(Contract.Result<string>() != null); var result = x + "ほげ"; return result; }
例えば上記の例では、クライアントはnullではない文字列を引数に渡す義務があり、
クライアントは、サプライヤからnullではない文字列を戻り値として返してもらうという利益がある、
という契約がなされています。事後条件の契約が、メソッドの上の位置に記述されていますが、
これはコンパイラによって自動的にreturn文の直前にチェックが行われます。
(Contract.Result() 関数は関数の戻り値を参照するときに使われます。)
クラス不変表明(不変条件)
クラス不変表明とは、クラスについてどんな操作を施しても、常に満たされているべき条件を定めたものです。ただし、この常にとはいついかなる時もという意味ではなく、オブジェクトが完了している状態でという意味である。
つまり、関数が呼び出されて、ルーチンワークが目的の事後条件(結果)へ向かっている間は満たされなくても構わない。
Code Contractsにおいては、ObjectInvariant(invariantは不変という意味)メソッドという仕組みを使います。
クラス不変表明を表すメソッドを表すには、ContractInvariantMethod 属性でマークします。
また、「ObjectInvariant」という名前にして、直接呼び出すことができないようにするために
アクセシビリティを protected にするのが一般的なプラクティスとなります。
[ContractInvariantMethod] protected void ObjectInvariant() { Contract.Invariant(_strategy != null); }
このクラス不変表明を表現している特別なメソッドは、クラスそのもの「正しさ」を契約として表現していて、
オブジェクトのすべてのメソッドの最後にこの契約がチェックされ、オブジェクトに契約違反がないか確認されます。
ここで大事なのはこれを全てのメソッドつまり、関係するサブクラスのメソッドも含めて契約が適用されるということ。
クラスの深い意味的な特徴を捉えて整合性を強制することになるので、設計は慎重にするとよい。
慣れないうちは、厳格であったり複雑なな契約を強要することは避けた方がいいだろう。
インターフェイス契約
Code Contractsでは、インターフェイスに対して契約を表明することができる。インターフェイスに関連付けられた個別のクラスを属性として指定することで利用することができる。
例えば以下のように宣言する。
[ContractClass(typeof(IFooContract))] interface IFoo { int Foo(int x); } [ContractClassFor(typeof(IFoo))] sealed class IFooContract : IFoo { int IFoo.Foo(int x) { Contract.Requires(x > 0); Contract.Ensures(Contract.Result<int>() > 0); throw new NotImplementedException(); } } public abstract class AbstractFooImplSample : IFoo { public virtual int Foo(int x) { return x; } }
ContractClassForAttributeによって、特定のインターフェイス(ここではIFoo)に対応する
インターフェイス契約を宣言するクラスをマークします。
ContractClassAttributeによって、そのインターフェイス契約をインターフェイスに対して適用します。
これで、インターフェイスに対する契約を関連付けすることが出来ます。
契約による設計は性悪説的思想である
契約による設計は「性悪説」に基づいた思想です。契約による設計を行わない場合、「使う側がルールを守って使ってくれるはず」、
あるいは「実装を見て配慮して使ってくれるだろ常識的に考えて」という「性善説」によって成り立っているのに対し、
契約による設計では「人間なんてミスする生き物だし、提供側が考えてやんなきゃだめでしょ」
「使う側なんてどうせ何も考えてるわけないじゃん」という「性悪説」を前提とした考え方です。
何かすこしネガティブな考え方のような感じですが、頑丈なソフトウェアを作るという目標に対しては、
とてもポジティブな考え方であると言えます。
これはフール・プルーフという工業製品や電化製品などで、
利用者が誤って不適切な操作を行なっても危険を生じない、あるいは正常な動作を妨害されないように、
設計の段階で安全対策を施しておくという考え方に似ています。
例えば、正しい向きにしか入らない電池ボックスや、ドアを閉めなければ加熱できない電子レンジ、
底面が床についているときは加熱しないアイロンなど、人が犯しうるミスを予め想定して設計することで、
利用者に対して優しい製品となっています。性悪説を前提とすることで、良い結果が生まれています。
「人間はミスするもの」、「人間の注意力はあてにならない」という前提を持つことが、
システムの安全設計の基本として重要な基本概念であると言えます。
契約による設計は、防御的プログラミングとはまったく逆のアプローチ
契約による設計のアプローチは個々の関数について理論的な基盤をもつことで、堅牢でかつモジュール化されたシンプルなシステム開発をするためのガイドラインを提供します。
これは面白いことに、ある意味では防御的プログラミングとは全く逆のアプローチです。
防御的プログラミングでは、引数がnullではないとか、パラメータの値が有効範囲内であることだとか、
できるだけ多くのチェックを行うことによって各モジュールを保護するという方針で進められる。
しかし、この防御的プログラミングという方法ではオブジェクト間の義務と利益。その責任の所在が曖昧になりやすい。
これは、ソフトウェアの複雑さを招く副作用があることを示している。
それに対して、契約による設計は、誤りやすい一貫性の条件を明確に指定して
それを守る責任をルーチンの呼び出し元(クライアント)と、ルーチンの実装(サプライヤ)に明示的に割り当てることで、
オブジェクト間の義務と利益にかかわる複雑性について、明確にすることができる。
つまりこれは、契約を用いることでインターフェイス仕様が強力で且つ内容の深いものになることを意味します。
そして、事前条件で契約されている事項については、すでに保証されていることなので、
防御的プログラミングを用いて再度ルーチン内でチェックをしてはいけない(する意味がない)ことも意味しています。
契約による設計における下請け契約(subcontractiong)の概念を理解する。
契約による設計においける下請け契約の概念は、オブジェクト指向の継承関係(is-aの関係)と似ています。あるオブジェクトの仮想関数における契約が、継承先で再定義されるとき、
その仮想関数における契約について下請けに出されると考えらる。
つまり、再定義された仮想関数が少なくともオリジナルの契約を満たさなければならない
という点に置いて、再定義された関数は契約についてもセマンティクスを保持した変換である必要があります。
また、再定義された関数がオリジナルの契約を満たすだけではなく、それ以上のことを行ってもよいことを意味します。
たとえば、オリジナルの契約で否定していた条件を許容したり、オリジナルの契約よりも
より優れた結果を返す事後条件を契約とするなどです。
つまり、契約による設計における下請け契約の考え方では、
サブクラスによって再定義された事前条件は、より広くを受け入れてオリジナルの契約よりも厳しくあらず、
事後条件は、より多くのことを行い、オリジナルの契約よりも強化されなければなりません。
オブジェクト指向言語Eiffelでは、契約による設計と継承を統合するための規則が言語レベルで適用されていて、
require elseおよびensure thenという構文において、事前条件は弱められ、
事後条件は強化されることを保証するようにできています。
しかし、.NET Framework4で提供されるCode Contractsでは、この概念はどうも保証されないようです。
つまり、Code Contractsにおいては、事前条件をオリジナルの契約よりも強くすることが可能だし、
事後条件を弱めることも可能なようになっているようです。
これは、ある意味では自由度の高さを表わしているが、厳密な契約による設計の概念とはズレていると言えるでしょう。
Code Contractsの利用者は、契約による設計について、
誤りをおかす可能性が含まれていることを、あらかじめ理解しておく必要があります。
契約による設計とテスト駆動開発(TDD)
契約による設計では、問題を引き起こすコードを早期発見して即座に対処することができる。そういう意味では、ユニットテストを頻繁に行うテスト駆動開発に相通じる戦略である
と考える人も多いかもしれない。が、その趣旨や性質はテスト駆動開発のそれとは異なる。
テスト駆動開発はテストやそのオブジェクトの表明を保証をするための技法ではなく、開発方法あるいは設計技法です。
それに対して、契約による設計は仕様そのものであり、ソフトウェアの安全性を高めようという技法である。
つまり、そのアプローチと求めている結果がまったく異なる技法なのです。
テスト駆動開発を行う場合、契約による設計を意識して併用して行うことで、
よりクライアント側からの使われ方を意識したインターフェイス。オブジェクトの責務やリレーションを意識したインターフェイス
が期待できます。また、契約を盛り込むことで、テスト駆動を行う上でのTODO事項や、考慮漏れへの気付き。
あるいは、準備すべきテストコードの考えがまとめやすくなる。テストの資産価値を評価しやくなる。などの効能が期待できる。
設計者が契約による設計をためらう場合、そしてその対処法
契約による設計は堅牢なコードを保って信頼できるソフトウェアを作る上でとても強力な技法だが、場合によっては適用が難しい場合があり、それをためらってしまうこともあるだろう。
マーチン・ファウラ大先生がblikiの「実例による仕様書」内で語っているように、
すべての仕様について契約としてコードに盛り込むというのは、現実問題として難しい場合もある。
クラスの責務によっては、複雑になったり表現が難しくなることも少なくない。
条件式の計算内容がヘビーだったり、本来のコードよりも契約の計算の方がトリッキーなんてことも珍しくないでしょう。
「すべての仕様について契約されている状態」が理想なのはもちろんではあるが、
「すべてについて契約が保証されているべきであり、必ず守られなければならない」ものだとは思わない。
単純なヒューマンエラーの防止やTDD支援的な用途として、契約を利用するというのもアリだと思う。
契約の条件が複雑であったり、契約をコードとして記述するにはとても割に合わないというような場合は、
オブジェクトの責務や設計に誤りがある可能性もあるので、再度設計について考えることを検討する。
検討してみたが、オブジェクトの設計に変更がないような場合もある。その場合は、
契約条件の詳細をコメントとして記述しつつ、防御的プログラミングのアプローチを適用する。
そのあたりはケースバイケースで対処するものだと思う。
実例による仕様書
http://capsctrl.que.jp/kdmsnr/wiki/bliki/?SpecificationByExample
最後に
ここ最近は契約にハマっていました。C#およびVB.NETで契約しまくって遊んでいました。*2契約はオブジェクト指向について、さらなる深みを与えてくれる重要な概念だと思っています。
Code Contractsは.NETで開発するソフトウェアの品質向上には絶対に欠かせない要素になると感じています。
契約楽しい!契約最高!です。みなさんも契約による設計を学んで、ぜひ有効活用しましょう。
いまさらですが、VB.NETで「関数名に代入的な記法」で戻り値を決定するのが“やっぱりキモい”件。
ハイクの方で書いたネタですが、結構おもしろいネタだったので、
加筆修正などしてこちらにも投下しておきます。
なんぞこの記法。VBがマゾ言語と言われる由縁のひとつがこれだ。
VBおよびVB.NETでは下記のような感じで関数を記述することができる。
(VB.NETでは新たにReturnによって戻り値を返すことも可能)
Sub Main() Debug.Print(Hoge(False)) Debug.Print(Hoge(True)) End Sub Function Hoge(ByVal fuga As Boolean) As String Hoge = "hoge" If fuga Then Hoge = "fuga" Exit Function End If End Function
生粋のVBプログラマだった自分が言うのもなんだが、VB.NETでこれやられると流石にキモい。
この関数名に代入的な記法は、どうも行儀がよくないと思う。
たぶん普通の「行儀の良い人」ならやらないと思うが、VB.NET(VB9以降)の場合、
関数名に対して拡張メソッドを呼び出すこともできてしまったりするから、更に行儀が悪い。
コードの可読性の低下を招くとともに、バグの温床となる臭いがぷんぷんします。
VB6までは関数名に代入的な記法でのみ、関数の戻り値を決定していました。
しかし、それを全く新しい言語に生まれ変わったVB.NETでまでサポートする必要はあったのだろうか。
(プロパティのgetterもプロパティ名に代入的な記法が可能ってのも・・)
GoSub〜Return構文の存在
VB6までは、そのご先祖様であるQuickBASICとかその他のBASIC言語と同じように、下記のような「GoSub〜Return」構文がサポートされていました。
Private Sub Main() Call Test End Sub Private Sub Test() Debug.Print("開始") GoSub hoge Debug.Print("終了") Exit Sub hoge: Debug.Print ("サブちゃんだよ!") Return End Sub
この構文を使うと、GoSubのところで継続位置が記憶され、hogeへジャンプし、
Returnで記憶したGoSub位置からまた処理が継続されて、上記のプログラムでは
開始 サブちゃんだよ! 終了
と出力されます。
これは古来からあるBASIC言語の名残のレガシー機能であり、構造化プログラミングの構文とはかけ離れたものです。
確かにVB6までもサポートされていましたが、そもそもVBでは構造化プログラミングをサポートしているので、
VBの時点でこの構文は既に不必要なものでした。なのでこの「GoSub〜Return」構文の存在を知らないVBプログラマも多いかもしれません。
関数名に代入的記法はVB.NETにも本当に必要だったのでしょうか?
VB.NET設計者は.NETファミリ言語として、VB.NETをC#と同系統のオブジェクト指向言語にすることを目指しました。そして、非直観的で整合性を乱すような「GoSub〜Return」構文などのレガシー機能の一部をサポートをしないことに決定しました。
その代わりに、Returnに別の意味を持たせました。「関数名に代入的な記法」に代わるC系言語でお馴染みのやつです。
VB6まではReturnは別の意味(GoSubで記憶した位置に戻る)で使われていましたが、VB.NETではそれが変わったのです。
つまり、この決定が下されたタイミングで「関数名に代入的な記法」は事実上不必要となったと言えます。
にもかかわらず、なぜVB.NET設計者はこの関数名に代入的な記法を残したのでしょう。不思議です。
もちろん、マイグレーション的な意味で残したであろうことは想像に難しくありませんが、
中途半端に残されたレガシー機能であること。可読性も低下しバグの温床となりやすいこと。
IL(中間コード)に落とされたときに最適化されず、余計なコードが吐かれる(暗黙的に変数が宣言されるなど)のでパフォーマンスが低下すること。
などの様々な観点からみて、VB.NETの設計「どうしてこうなった」感がぬぐいきれません。
私が設計者だとしたら、思い切って「関数名に代入的な記法」はすっぱり切り捨てたと思う。
だから関数名に代入的な記法で戻り値を決定する仕様がVB.NETに残されたことは、とても「キモい」と思うし、それを使う行為もまた「キモい」。
マイクロソフト的にも、関数名に代入の記法は使わないでねという指針が示されています。
(だったらサポートすんなって言いたい。もしくはオプションで警告またはエラーにできるとか。)
以下のあたりが参考になると思われます。
VB 6.0 ユーザーのための VB .NET 移行ガイド - プロシージャ
http://msdn.microsoft.com/ja-jp/library/dd297703.aspx
VB 6.0 ユーザーのための VB .NET 移行ガイド - レガシー機能
http://msdn.microsoft.com/ja-jp/library/dd297708.aspx
ということで、VB.NETではこの「関数名に代入的な記法」は使うべきではありません。
マイグレーションの場合、規模によっては工数的な問題で適用が難しく仕方なく目をつぶる・・ことはあるとして(本当はつぶっちゃダメだよ!)。
新規プロジェクトであればVB.NETで「関数名に代入的な記法」はコーディング規約で禁止するなり、
コードレビューなどで精査して、断固禁止とすべきでしょう。ちゃんとしたところでは、そうしていますよ。
ちなみに
過去にDelphiを2年ほど使ってましたが、Pascal系のResult代入な記法も似たような感じで私はきもく感じました。まぁそれ以前に「begin〜end」が目障りすぎだろうという噂もありますがw
なんぞこれ。コンストラクタパラメータが異常に大杉る・・・。バカなの?死ぬの?そういう場合はBuilderパターンを検討してみよう。
(追記:2009/11/18)
id:Nobuhisaさんにお返事を頂きました。ありがとうございます!
Effective JavaのアレをC#で - (hatena (diary ’Nobuhisa))
C#でBuilderを実装するにあたって、とても参考になりますので、こちらもぜひご覧ください。
引数大杉。Builderパターンだろう常考
異常にコンストラクタパラメータの多いクラスに直面して辟易することがある。そう、あるんです。そのうち必須パラメータが3個で、任意指定のパラメータが10個・・・。え、まじで?
驚きを禁じ得ない。。。作った奴は一体何を考えているのだろうか。
おそらく何も考えていないに違いない。なんてこと、あるよね?あるよね?
そういった痛いクラスを悪気もなく量産してくれちゃうような方には、
ぜひ、「Effective Java 第2版」にてJoshua Bloch氏が提案している、
「数多くのコンストラクタパラメータに直面した時にはビルダーを検討する」を読んで頂こう。
この本はその名のとおり実際Java使いによるJava使いのためのJava本なのだが、
Java使いではなくとも学ぶ事の多い良書なので、プログラマであればぜひ読んでおきたい。
このEffective JavaでJoshua Bloch氏が提案しているBuilderパターンは、
Pythonに見られる名前付きオプションパラメータを模倣してJavaで実装したたもので、
コンストラクタでのパラメータ指定をした場合と同様に、
そのパラメータに対する不変式を強制することができるというもの。
なので、コンストラクタやstaticなファクトリメソッドが多くのパラメータを
持つようなクラスを設計する場合、このパターン(というかイディオム)を適用するととても具合がいいというお話。
具体的には、生成したいオブジェクトのインスタンスをnewで直接生成する代わりに、
ビルダーオブジェクトを作って、生成したいオブジェクトが必要とする任意パラメータを
ビルダーに対して個別に設定しておいて、最後にビルダーに対して、
生成したいオブジェクトをインスタンス化する処理を委譲するような方法をとります。
C#でJoshua Bloch氏のBuilderパターン
C#はJavaとは言語仕様が当然異なるので、Javaと全く同じようには書けないが、このBuilderパターンをC#で実装する例を書きたいと思う。
いろいろな書き方があると思うが、自分は例えばこんな風に書いていたりします。
以下、C#によるBuilderパターンのサンプルコード。
using System; namespace ConsoleApplication1 { public class BuilderSample { private readonly int necessary1; private readonly int necessary2; private readonly int optional1; private readonly int optional2; private readonly int optional3; public sealed class Builder { public int necessary1 { get; private set; } public int necessary2 { get; private set; } public int optional1 { get; private set; } public int optional2 { get; private set; } public int optional3 { get; private set; } private Builder() { } public Builder Optional1(int val) { this.optional1 = val; return this; } public Builder Optional2(int val) { this.optional2 = val; return this; } public Builder Optional3(int val) { this.optional3 = val; return this; } public BuilderSample Build(int necessary1, int necessary2) { this.necessary1 = necessary1; this.necessary2 = necessary2; return new BuilderSample(this); } public static Builder Instance { get{ return new Builder(); } } } private BuilderSample(Builder builder) { this.necessary1 = builder.necessary1; this.necessary2 = builder.necessary2; this.optional1 = builder.optional1; this.optional2 = builder.optional2; this.optional3 = builder.optional3; } public override string ToString() { string result = ""; result += this.necessary1.ToString() + ":"; result += this.necessary2.ToString() + ":"; result += this.optional1.ToString() + ":"; result += this.optional2.ToString() + ":"; result += this.optional3.ToString(); return result; } } }
BuilderSampleクラスのコンストラクタがprivateである点に注目です。
今回は、Buildメソッドで必須パラメータを受け取ることにして、
builder インスタンスを使いまわして必須パラメータの決定を遅らせる風味にしてみました。
Builder内の各プロパティのgeterがpublicなので、外に見えてしまっているところが
少々不格好ですが仕方ありません。もっと良い書き方があれば教えてください。
で、上記のようにBuilderパターンで実装しておくと、下記のようにインスタンスを生成することができます。
using System; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var sample1 = BuilderSample.Builder.Instance.Optional1(100).Optional3(35).Optional2(27).Build(100, 204); var sample2 = BuilderSample.Builder.Instance.Optional3(33).Build(450, 56); var sample3 = BuilderSample.Builder.Instance.Build(10, 10); Console.WriteLine(sample1); Console.WriteLine(sample2); Console.WriteLine(sample3); Console.ReadKey(); } } }
実行結果
100:204:100:27:35 450:56:0:0:33 10:10:0:0:0
多くの任意のパラメータが必要になるようなケースであれば、
すべてをコンストラクタパラメータで渡してしまうよりも、間違いなくこのパターンを採用した方がうまくいくでしょう。
非常にシンプルでわかりやすいイディオムなので、すぐに覚えられると思います。
そもそも引数大杉とか不吉な臭い充満なわけで
OCamlの統計に、こんなのがあるんです。
OCamlのライブラリの2239関数のうち高階関数を使ったものがそもそも12.8%だけ。
関数引数を1つだけ取るものが12.1%で、2つ以上の関数を取るものは0.7%しかないというんです。
というような統計もあるくらいなので、まともな設計をしていれば、
そもそも関数の引数はせいぜい2、3個程度で間に合うことが殆どである。というのは事実でしょう。
なので、確かにBuilderパターンはこのような問題を解決するための良いテクニックのひとつではあるのですが、
盲目的に何でもかんでもBuilderパターンを適用しとけばおk!というわけでは当然ありません。
例えば引数を4つ以上とるような関数があった場合、何かしら不吉な臭いがないか疑いを持つ必要はあると思う。
「そもそもそんなにたくさん引数いるんかい?いらないんじゃないかい?」と。
そんなときは、関数ひとつひとつについて再度見直すのもいいんじゃないかなあ、なんて思ったりする今日この頃です。
F#でもオブジェクト指向したい。手始めにC#で書いたUndo,Redoの実装を移植してみました。あれ?F#にはprotectedアクセシビリティないの?ぐはぁ
F#は.NETの第一級言語だからオブジェクト指向だって余裕でできるよ
関数型の考え方に基づいて、汎用的に使えるUndo,Redo機能の実装しようと考えたとき、関数型言語のノウハウがない自分には、シックリくる良い案が全く思い浮かびませんでした。
F#は関数型パラダイムを中心とした言語ですが、
.NET向けの第一級言語ということもあり、オブジェクト指向もしっかりサポートされています。
ということで、現時点では「それオブジェクト指向で書けるなら、
別に関数型のパラダイムにこだわらなくてもいんじゃね?」という結論に至りました。
せっかくのマルチパラダイムなんだから、F#でオブジェクト指向したっていいよね、と。
初心者なりにF#で多少プログラミングを勉強してきましたが、
F#を使ってオブジェクト指向的に、インターフェイスやクラスを定義したことは
ほとんどありませんでしたので、練習を兼ねて以前C#で書いた
Undo,Redoの実装って何回かしかやってない気がする。ジェネリックなCommandパターンとMementoパターンの応用で大体いけそうな気がする。 をF#に移植してみました。
F#でオブジェクト指向的にUndo,Redoを実装してみよう
F#の仕様上、protectedなアクセシビリティはないようなので、
非対称アクセサのアクセシビリティを用いてprotectedを設定できませんでしたが、
その他については、C#版からそれなりに忠実に移植できていると思います。
以下、C#版から移植したF#のサンプルコードです。
#light namespace Library1 open System open System.Collections.Generic ///ICommandインターフェイス type ICommand = ///呼び出し abstract Invoke: unit -> unit ///元に戻す abstract Undo: unit -> unit ///やり直し abstract Redo: unit -> unit [<AbstractClass>] ///記念品(笑)抽象クラス type Memento<'a,'b>() = ///思い出データを取得または設定します。 [<DefaultValue(false)>]val mutable _MementoData : 'a member this.MementoData with get() = this._MementoData and set(value) = this._MementoData <- value ///データ反映対象オブジェクトを取得または設定します。 [<DefaultValue(false)>]val mutable _Target : 'b member this.Target with get() = this._Target and set(value) = this._Target <- value ///思い出を反映させます。 abstract SetMemento: 'a -> unit [<Sealed>] ///思い出更新コマンド type MementoCommand<'a,'b> = val mutable private _memento : Memento<'a,'b> val mutable private _prev : 'a val mutable private _next : 'a new(prev : Memento<'a,'b>, next: Memento<'a,'b>) = { _memento = prev; _prev = prev.MementoData; _next = next.MementoData;} interface ICommand with ///呼び出し member this.Invoke() = this._prev <- this._memento.MementoData this._memento.SetMemento(this._next) ///元に戻す member this.Undo() = this._memento.SetMemento(this._prev) ///やり直し member this.Redo() = this._memento.SetMemento(this._next) [<Sealed>] ///CommandManager type CommandManager = val private _maxStack : int val private _undoStack : Stack<ICommand> val private _redoStack : Stack<ICommand> ///コンストラクタ new() = {_maxStack = System.Int32.MaxValue; _undoStack = new Stack<ICommand>(); _redoStack = new Stack<ICommand>(); } ///コンストラクタ new(maxStack) = {_maxStack = maxStack; _undoStack = new Stack<ICommand>(); _redoStack = new Stack<ICommand>(); } ///呼び出し member this.Invoke (command : ICommand) = if this._undoStack.Count >= this._maxStack then false else command.Invoke () this._redoStack.Clear () this._undoStack.Push command true ///元に戻す member this.Undo = if this._undoStack.Count = 0 then () else let command = this._undoStack.Pop () command.Undo () this._redoStack.Push command ///やり直し member this.Redo = if this._redoStack.Count = 0 then () else let command = this._redoStack.Pop () command.Redo () this._undoStack.Push command member this.Refresh = this._undoStack.Clear () this._redoStack.Clear ()
短っ。なんかC#に比べてやたら簡潔に書けた気がするんだが。気のせいですかねw
クライアント側の実装
F#で画面作るのは面倒くさいのでw、クライアント側はC#で。
C#版のをちょこっと変えただけです。
using System; using System.Windows.Forms; using Library1; namespace WindowsFormsApplication1 { public partial class Form1 : Form { private Memento<string, TextBox> _memento; private CommandManager _cmdManager = new CommandManager(); public Form1() { InitializeComponent(); _memento = new TextBoxMemento(this.textBox1.Text, this.textBox1); } private void btnSave_Click(object sender, EventArgs e) { var current = new TextBoxMemento(this.textBox1.Text,this.textBox1); var cmd = new MementoCommand<string,TextBox>(_memento,current); if (!_cmdManager.Invoke(cmd)) { MessageBox.Show("状態の最大保存数を超えました。"); return; } _memento = current; } private void btnRefresh_Click(object sender, EventArgs e) { _cmdManager.get_Refresh(); } private void btnUndo_Click(object sender, EventArgs e) { _cmdManager.get_Undo(); } private void btnRedo_Click(object sender, EventArgs e) { _cmdManager.get_Redo(); } } }
CommandManagerに対する呼び出しが、
get_Refresh();get_Undo();get_Redo();に変わっているところに注目です。
using System.Windows.Forms; using Library1; namespace WindowsFormsApplication1 { /// <summary> /// TextBoxの思い出具象クラス /// </summary> public sealed class TextBoxMemento : Memento<string, TextBox> { /// <summary> /// コンストラクタ /// </summary> /// <param name="mementoData">思い出データ</param> /// <param name="txtBox">データ反映対象オブジェクト</param> public TextBoxMemento(string mementoData, TextBox target) { this.MementoData = mementoData; this.Target = target; } /// <summary> /// ターゲットに思い出を反映させます。 /// </summary> public override void SetMemento(string mementoData) { this.MementoData = mementoData; this.Target.Text = mementoData; } } }
変更はありません。
using System; using System.Windows.Forms; namespace WindowsFormsApplication1 { static class Program { /// <summary> /// アプリケーションのメイン エントリ ポイントです。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
変更はありません。
以上です。お疲れさまでした。いい練習になりました。
関数型言語のF#で、わざわざオブジェクト指向なんてしなくていいだろう常考
なーんて意見もありそうな気もしますが、私はそうは思いません。
まぁ、protectedなアクセシビリティがないのはちょっと致命的かもしれませんけどね(;´ω`)
なんぞこれ。NaNってなんなんすか?あのね、非数のことだよ。なんだかナンのカレーが食べたくなるね。ヒ素じゃなくてよかった。
ネタ元
びっくりした事 - えムナウBlog (わんくま同盟Blog's)
http://blogs.wankuma.com/mnow/archive/2009/10/14/182100.aspx
以下のコンソールアプリケーションで"NG"と表示するケースがあった。
class Program { static void Main(string[] args) { double d1 = ある計算式; double d2 = d1; Check(d1, d2); } private static void Check(double d1, double d2) { if (d1 == d2) { Console.WriteLine("OK"); } else { Console.WriteLine("NG"); } } }
非常にびっくりしたのでクイズにします。
ある計算式とは何でしょう?
一番簡単(文字数が少なく)に書いてみて!
結論から言うと、このコードで"NG"が出力される「ある計算式」とは、
演算結果が「double.NaN」になるようなものです。
NaN (Not a Number)*1とは結果が存在しない演算の結果(非数)として得られるもので、
たとえば、.0/0ですとか、0/0Dなどが演算結果が得られないので非数ということになります。
えムナウさんが「大文字を別解とすると 28パターン(最短文字数の4文字で書く場合)」とおっしゃっていますが、
それは誤りです。自分も間違えないように覚書としてチラ裏に記録することにしました。
コメント欄にて、「どうでもいい別解」と書かせていただいたのは、パターンが多すぎたためです。
それ本当に28パターンだけですか?
では、NaNなんパターンあるのでしょうか。少し酔っ払っているので、ちょっと自信がありませんが、
この問題のコードでは、127パターンあると思います。以下C#のコードで示します。
using System; using System.Collections.Generic; namespace ConsoleApplication1 { class Program { enum testResult { OK, NG } static void Main(string[] args) { int count = 0; foreach (var dd in GetTestData()) { double d2 = dd; if (Check(dd, d2) == testResult.NG) { count++; } } Console.WriteLine(count); Console.ReadKey(); } private static testResult Check(double d1, double d2) { if (d1 == d2) { Console.WriteLine(testResult.OK); return testResult.OK; } return testResult.NG; } private static IEnumerable<double> GetTestData() { //.0 yield return 0 / .0; yield return 0 % .0; yield return .0 / 0; yield return .0 % 0; yield return 1 % .0; yield return 2 % .0; yield return 3 % .0; yield return 4 % .0; yield return 5 % .0; yield return 6 % .0; yield return 7 % .0; yield return 8 % .0; yield return 9 % .0; //d yield return 0 / 0d; yield return 0 % 0d; yield return 0 / 0D; yield return 0 % 0D; yield return 0d / 0; yield return 0d % 0; yield return 0D / 0; yield return 0D % 0; yield return 1 % 0d; yield return 2 % 0d; yield return 3 % 0d; yield return 4 % 0d; yield return 5 % 0d; yield return 6 % 0d; yield return 7 % 0d; yield return 8 % 0d; yield return 9 % 0d; yield return 1 % 0D; yield return 2 % 0D; yield return 3 % 0D; yield return 4 % 0D; yield return 5 % 0D; yield return 6 % 0D; yield return 7 % 0D; yield return 8 % 0D; yield return 9 % 0D; yield return 1d % 0; yield return 2d % 0; yield return 3d % 0; yield return 4d % 0; yield return 5d % 0; yield return 6d % 0; yield return 7d % 0; yield return 8d % 0; yield return 9d % 0; yield return 1D % 0; yield return 2D % 0; yield return 3D % 0; yield return 4D % 0; yield return 5D % 0; yield return 6D % 0; yield return 7D % 0; yield return 8D % 0; yield return 9D % 0; //f yield return 0 / 0f; yield return 0 % 0f; yield return 0 / 0F; yield return 0 % 0F; yield return 0f / 0; yield return 0f % 0; yield return 0F / 0; yield return 0F % 0; yield return 1 % 0f; yield return 2 % 0f; yield return 3 % 0f; yield return 4 % 0f; yield return 5 % 0f; yield return 6 % 0f; yield return 7 % 0f; yield return 8 % 0f; yield return 9 % 0f; yield return 1 % 0F; yield return 2 % 0F; yield return 3 % 0F; yield return 4 % 0F; yield return 5 % 0F; yield return 6 % 0F; yield return 7 % 0F; yield return 8 % 0F; yield return 9 % 0F; yield return 1f % 0; yield return 2f % 0; yield return 3f % 0; yield return 4f % 0; yield return 5f % 0; yield return 6f % 0; yield return 7f % 0; yield return 8f % 0; yield return 9f % 0; yield return 1F % 0; yield return 2F % 0; yield return 3F % 0; yield return 4F % 0; yield return 5F % 0; yield return 6F % 0; yield return 7F % 0; yield return 8F % 0; yield return 9F % 0; //e yield return 0 / 0e; yield return 0 % 0e; yield return 0 / 0E; yield return 0 % 0E; yield return 0e / 0; yield return 0e % 0; yield return 0E / 0; yield return 0E % 0; yield return 1 % 0e; yield return 2 % 0e; yield return 3 % 0e; yield return 4 % 0e; yield return 5 % 0e; yield return 6 % 0e; yield return 7 % 0e; yield return 8 % 0e; yield return 9 % 0e; yield return 1 % 0E; yield return 2 % 0E; yield return 3 % 0E; yield return 4 % 0E; yield return 5 % 0E; yield return 6 % 0E; yield return 7 % 0E; yield return 8 % 0E; yield return 9 % 0E; } } }
実行結果
127
おっ!ナイスフリードマン数!(何
ということで、28パターンではありませんでした。
C#において、NaN(非数)となる除算演算と剰余演算とは
では、C#ではどのような演算を行うとNaN(非数)となるのでしょうか。
はい、そのあたりの詳しい仕様については、MSDNにちゃんと書いてあります。
C# 言語の仕様
7.7.2 除算演算子
http://msdn.microsoft.com/ja-jp/library/aa691373(VS.71).aspx
商は、IEEE 754 の演算規則に従って計算されます。次に示す表は、非ゼロ有限値、
ゼロ、無限大、および NaN の可能なすべての組み合わせの結果をまとめたものです。
x および y は正の有限値で、z は x / y の結果です。
結果を格納する型に対して結果が大きすぎる場合、z は無限大になります。
結果を格納する型に対して結果が小さすぎる場合、z はゼロになります。
7.7.3 剰余演算子
http://msdn.microsoft.com/ja-jp/library/aa691374(VS.71).aspx
次に示す表は、非ゼロ有限値、ゼロ、無限大、および NaN の
可能なすべての組み合わせの結果をまとめたものです。x および y は正の有限値です。
z は x % y の結果で、x - n * y を計算して得られます。
n は、x / y より小さいか等しい中で最大の整数です。
この剰余計算方法は、整数オペランドに対して使用されるものとほぼ同じですが、
IEEE 754 の定義 (n は x / y に最も近い整数) とは異なっています。
ということでした。浮動小数値の演算を扱うような場合は注意したいですね。
あとふざけたタイトルですいません(思いついちゃったんだからしょーがない。)
(追記:2009/10/17 10:12)
ちなみに、指定した数値がNaN (非数)であるかどうかを評価するには、
double.IsNaNメソッドを用います。
http://msdn.microsoft.com/ja-jp/library/system.double.isnan.aspx
「スマートタグの表示」のショートカットキーの既定値がShift+Alt+F10というのは使いづらい件。Shift+Alt+Zでよくね?
Visual Studio2005以降では、Eclipseなんかに比べるとまだまだ貧弱とはいえ、
便利なリファクタリング機能がいくつかある。たとえば、フィールドやメソッド名を変更するとき、
スマートタグを表示することで、参照されているものすべての名称を一括して変更することができる。
あるいは、クラスやインターフェイスに対してメソッドのスタブを作成したり、
インターフェイスの実装スタブを作成したり、足りないusing を自動でファイルの先頭に追加できるなど、
開発効率を向上させる便利な機能がいくつか用意されている。
まだ活用されていない方は勿体無いので意識的にガンガン活用するとよいと思う。
とはいえ、C#では利用可能であっても、なぜかVB.NETでは使えないリファクタ機能が多く、
VB.NETユーザーの方は、指を咥えて我慢するしかないという悲しい現実はあります。
この便利なリファクタ機能を利用するための、スマートタグを表示させる方法のひとつとして、
文字列の末尾にマウスカーソルを合わせてクリックするという方法があります。
しかし、いちいちあのような狭い範囲にマウスカーソルを重ね合わせてクリックするという操作は
非常に面倒くさく、効率がよろしくない。ですので、頻繁にVisual Studioのリファクタ機能を使うような開発者であれば、
ほとんどの場合キーボードのショートカットキー操作で表示させたくなります。
そこで既定値として割り当てられているショートカットがShift+Alt+F10というわけです。
これは、開発者が頻繁に使うことを想定した設定とはとても思えません。
個人差はあると思われますが、両手での操作が必要になるので、非常に押しにくいショートカットだと思う。
自分は、リファクタ機能を使うときにこのスマートタグ表示を頻繁に利用するので、Shift+Alt+Zに設定しています。
この設定であれば、左手のキー操作とEnterキーでの確定操作だけで、手軽にリファクタ機能が利用できて、ちょっと幸せです。