配列のコピー

最近は仕事があまりなくて、そういうときは新しい技術に触れたり、社内ツールの調整をしたり、洞窟物語を進めたり、個々人で時間を自由に使えるようにしたり、ちょっとした内容で脱線した話題でホワイトボードを使ったミニ会議をしたりと、技術者なりの過ごし方があるようです。
MSDN フォーラムを眺めていたとき、そんな「脱線会議」の1つと同じような話題がありました。とはいえ、もっと単純な配列のコピーだったのですが、それなりに脚色して書くと

新人:配列のコピーってループ回せばいいのですかね?
担当:いいんじゃないかな。xxxx ネームスペースの yyyy あたりに static method でも用意しておいて
新人:わかりました
私:・・・。
(十数分後)
私:さっきのメソッド、どんなかんじになった?
新人:え?

こんなかんじで、脱線は始まったのです。

先に断っておくと、別に上の MSDN フォーラムのやりとりに問題があるとか文句があるという話ではありませんよ(苦笑
作成されたメソッドは、非常に単純で

static void Copy<T>(T[] source, T[] dest, int source_index, int dest_index, int length)
{
  for (int i = 0; i < length; i++)
    dest[dest_index + i] = source[source_index + i];
}

というようなかんじだったと思います。担当に問題点を聞き出します。

私:担当くん、この実装に問題は?
担当:えー? だ、大丈夫じゃない・・・ですか?(自身がなさげ)
私:例えば、length が配列の長さに対して非常に大きかったらどうなる?

いや、別に length の問題で IndexOutOfRangeException が発生して、それがそのままこのメソッドの外側に通知されるというような例外の再送出のない設計は私もよくやったりして、外側からみて直感的に判断できる例外であれば問題ないと思ってはいるんですけどね。

私:他には、2つの配列 source と dest が同じインスタンスで、コピー範囲がかぶっていたらどうなる?
担当:後ろからコピーしないとダメですね。使う人が同じ配列を渡したいときに気にしてくれるとよいのですが
私:Copy() を使うときに、2つの配列のインスタンスが同じであることを意識していない場合もあるよね
担当:えーっと、
私:たとえば…、配列をメンバー list として持つ型のインスタンス a, b、えーっと

class Person
{
  Event[] Schedules;
}

みたいなかんじのものがあったとして、特定の誰か2人を指定してスケジュールを複製する処理を先程の Copy を使っていて、a さんと b さんが、かならず同じスケジュールを共有する前提で Schedules の指すインスタンスが同じだったりしたら?

void CopySchedule(Person source, Person dest, DateTime date, int src_index, int dest_index, int length)
{
  Copy(source.Schedules, dest.Schedules, src_index, dest_index, length);
}

みたいなメソッドを書いている人は、これが同じ配列のインスタンスを渡している可能性があることを意識していないよね?

なかなかありえない話だが、こういうときにウマイ例が思いつかないので仕方が無い。

しかし、こういうところで「再利用されるメソッドの実装」に対する慎重さをかんじてもらわないと、こういった小さな問題が「不具合の種」として埋め込まれていくことで、それが前面にでてきたときに苦労するのは自分達なんですよね。

他にも null チェックが必要だとか、配列の長さに収まるかチェックが必要だとか、3人で問題点をあげつつコードを書き足していきました。最終的には、Array クラスのメソッドを使えという話になったんだけど、

今回のレビューで、この Copy() はそれなりに問題がないものになったかもしれないけれど、今後のメンテナンスで新しい人が入ってきたり担当者が替わったりしたとき、このメソッドの機能や信頼性についての情報を引き継ぐよりも、最初から Array クラスの複製メソッドを使っておけば、きちんと勉強している開発者なら説明は不要だし、MSDN のヘルプに動作について詳しく書かれているから、どんな用途で使用できて、どんな動きをするか調べやすいよね。

というかんじでまとめました。
新人くんは、この話の最中、いつも怒る担当くんが怒られているようで楽しそうでした(ぉぃぉぃ