matarillo.com

The best days are ahead of us.

31日間ReSharper一周 Day 22: Alt+Insでコード生成

2012-12-30 18:17:12

元記事

31日間ReSharper一周」の22日目にようこそ。

さて、Alt+Enterはネタにし終わった。全てはコード修正に関するものだ。これで新規コードが生成されるときもある(例えば、クラスがインターフェースを実装するように変更したときには、Alt+Enterはメソッドの実装を生成しないといけない)けど、焦点はエラーや警告を修正することにある。

今回のネタはAlt+Insだ。これははっきりとコード生成に狙いを定めたものだ。

Alt+Insメニュー

Alt+Insを押すとメニューが現れ、コード生成コマンドを全て表示する。このメニューはいつも全てのコマンドを表示し、利用できないものはグレーアウトされる(利用可能なものだけ表示する他のReSharperメニューとは対照的だ)。

Alt+Insはクラス内にコードを生成するので、カーソルがクラス内にないときにAlt+Insを押したらすべてがグレーアウトされる。

Alt+Insウィザードの共通機能

Alt+Insコマンドはすべてウィザードを表示する。ウィザードのページには一般的に、生成されるコードで考慮したいもの(たとえばフィールド)を選ぶことができるリストが含まれているだろう。

ウィザードページには2種類のリストがある。チェックリストと普通のリストだ。

上の通り、チェックリストには各々のアイテムの隣にチェックボックスがある。アイテムを0個以上選ぶことができるときに(つまり、「選択しない」ことに意味がある場合に)使われるようだ。

普通のリストにはチェックボックスがない。選択するときはリストからアイテムを1つ以上選ぶ。これは標準的な複数選択リストボックスだから、Shift+クリックやCtrl+クリックで複数のアイテムを選択できる。

普通のリストは、少なくとも1つのアイテムを選ばなければならないときに使われるようだ。しかし実際には強制されない。唯一選択されているアイテムをCtrl+クリックして選択解除し、ウィザードの次の画面に進むと、ちょっと面白い結果になるだろう。

もしリストのすべてを選択したいなら、簡単にできる。チェックリストでは、Ctrl+Aを押した後にSpaceを押す。普通のリストでは、単にCtrl+Aを押すだけだ。

コンストラクタの生成

このコマンドでは、クラスのフィールドをいくつかまたは全て初期化するための、引数付きコンストラクタが生成される。どのフィールドを初期化したいかを選ぶことができるチェックリストが表示される。

注意すべき機能:

  • 垂直掘り下げ。ベースクラスのコンストラクタが引数をとる場合は、新しく生成されるコンストラクタは自動的に同じ引数をとり、それをベースクラスのコンストラクタに渡す。これらの引数はパラメータリストの最初に置かれる。(後で、お好みでパラメータを並び替える機能について話そう。)
  • 複数コンストラクタ。ベースクラスに複数のコンストラクタがあるときは、どのコンストラクタを呼び出すかを尋ねる第2のウィザードページがある。2つ以上選んだら、新しく2つ以上のコンストラクタが生成される。
  • アホ。コンストラクタがすでに存在しているかどうかのチェックはない。すでに存在するのと同じ引数を持つコンストラクタを生成するような指示をすれば、よろこんでやってくれる。その結果コードがコンパイルできなくなるので赤い波線が表示される。良いコードが生成されるか確認しないたった2つのAlt+Insコマンドのうちの片方がこれだ。

プロパティの生成

コマンドは3つある。「読み取り用プロパティ」、「書き込み用プロパティ」、「読み書き用プロパティ」だ。動作は一緒。どのフィールドにプロパティを生成するかを尋ね、その通りにする。

この機能は賢い。すでにいくつかのプロパティを作成していたなら、それらのフィールドはウィザードで選択できない。もしすべてのフィールドにプロパティを作成していたなら、メニュー内でプロパティ生成コマンドが全部使用不可になる。

インターフェースメンバーの実装

この機能は実はAlt+Enterを使った「メンバーの実装」と同じものだけれど、こっちはインターフェースメンバーだけが表示され、抽象メソッドは表示されない。

継承メンバーのオーバーライド

これも同じで、Alt+Enterの「メンバーの実装」とほぼ同じだけれど、(a)インターフェースメンバーは表示されないことと、(b)抽象メソッドだけでなく、(非抽象の)仮想メソッドも表示することが違う。

Equals()とGetHashCode()もリストに表示されるけど、それをオーバーライドするにはもっとすごい方法があるって事を覚えておいてほしい――Alt+Insメニューにある「EqualsとGetHashCode」を使おう。下で記す。

委譲用メンバーの生成

アダプタデコレータを書いているとき、もしくはデメテルの法則に従ったコードを書きたいってだけのときでも、この機能は本当に便利だ。こんなコードが生成される。

public int Length
{
    get { return _inner.Length; }
}
public void GetFoo(FooSpec spec)
{
    return _inner.GetFoo(spec);
}
public void DoSomething()
{
    _inner.DoSomething();
}

この例では、LengthプロパティとGetFoo()およびDoSomething()メソッドを_innerフィールドへ委譲している。「委譲用メンバーの生成」ウィザードでは2~3回クリックすればすむし、一度に複数のメンバーができるし、ヒューマンエラーの傾向は手で書くよりもずっと少なくなる。

どんなフィールドやプロパティに対しても委譲することができる(ので、例えば遅延初期化される何かに委譲してもいい)。まずフィールドやプロパティを選び、そして次のページで、委譲したいメンバーを選ぶ。

「コンストラクタの生成」と同じくこの機能もアホだ。同じメソッドシグネチャを1度以上委譲できてしまう。そうするとコンパイルできないコードになってしまう。

EqualsとGetHashCodeの生成

EqualsとGetHashCodeをオーバーライドしなきゃいけないことはそんなにないけど、もしやるなら、自動化の支援があれば大歓迎だと思う。

ウィザードはまず、Equals()メソッドでどのフィールドを比べたいのか聞いてくる。次に、GetHashCode()の計算にどのフィールドを使うのかを聞いてくる。賢いことに、等価性の決定に使われていないフィールドがあったなら、そのフィールドはハッシュコードの計算に使わないほうがいいことを分かっている(つまり、最初のページでチェックしなかったフィールドは、2ページ目で利用できない)。

もし選んだフィールドに参照型があれば、ウィザードに3ページ目が存在し、非ヌルであることが想定できるフィールドがあるか聞いてくる。もしあれば、GetHashCode()の実装を単純にできる。

下は結果として生成されるコードの例だ。_nameは非ヌルであるとしている。_addressは違う。

public override bool Equals(object obj)
{
    if (this == obj) return true;
    Foo foo = obj as Foo;
    if (foo == null) return false;
    if (_x != foo._x) return false;
    if (_y != foo._y) return false;
    if (!Equals(_name, foo._name)) return false;
    if (!Equals(_address, foo._address)) return false;
    return true;
}
public override int GetHashCode()
{
    int result = _x;
    result = 29*result + _y;
    result = 29*result + _name.GetHashCode();
    result = 29*result + (_address != null ? _address.GetHashCode() : 0);
    return result;
}

31日間ReSharper一周 インデックスへ戻る