このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
さて、昨日のコードを再掲しよう。
LinkTargetHandler hander = null;
switch (pscd.pwszExt.ToLower()) {
case ".lnk":
hander = new LinkTargetHandler(
new ShortcutFile(pscd.wszFile).GetLinkTarget);
break;
case ".url":
hander = new LinkTargetHandler(
new InternetShortcutFile(pscd.wszFile).GetInternetShortcutTarget);
break;
default:
hander = new LinkTargetHandler(
new NullShortcut().GetOtherTarget);
break;
}
return hander();
クラスを利用するための初期化は個別に行い、
それを利用するタイミングをデリゲートを用いて統一した。
デリゲートのメリットは、同じシグネチャを持つメソッドを、
変数に記憶しておき、後で呼び出すことができることだ。
シグネチャさえ合っていれば、メソッドの名前や、
静的メソッド・インスタンスメソッドを問わずに
まったく同じように扱うことができるのも強みである。
デリゲートを経由で呼び出す実装側にには
メソッドのシグネチャ以上の制約を課す必要がないため、
クラスの状態変更通知などを他のクラスに送るための、
イベント・リスナーの仕組みで利用されることが多い。
ただ、デリゲート型の登録や、デリゲート変数の作成など、
その利用にある程度の手間がかかるのは事実である。
今回の要件では、switch の中でやることは決まっている。
そのため、デリゲートを使う方法は少し大掛かり過ぎるのだ。
こういった場合、デリゲートよりもシンプルな方法がある。
それは、「インタフェース」だ。
デリゲートが、メソッド 1 つに対する参照を持つならば、
インタフェースは、複数のメソッドに対する参照を持つ。
インタフェースは、クラスに対して機能し、
特定のシグネチャを持つメソッド(複数の場合もある)を
実装することを要求するのだ。
デリゲート型変数を経由してメソッドを呼び出せるのと同様、
インタフェース型変数を経由して、
インタフェースに規定されているメソッドを呼び出せる。
ただ、インタフェースはデリゲートと違い、
クラス側が、インタフェースを実装していることを、
クラス定義に明示する必要がある。
また、インタフェースは「契約」であるため、
インタフェースが規定しているメソッドを、
クラス側が「全て」実装する必要がある。
過去に出てきた IShellLinkW などもインタフェースだが、
これらは、シェル側が要求している契約である。
なお、6/27 にも IColumn インタフェースを作っている。
このときは説明もせずにさっさと作ったが、
あれも、Column を実装するための契約なのだ。
では、ショートカット用のインタフェース型を作ってみよう。
昨日作った ShortcutFile 等のクラスに共通する契約は、
「リンク先を文字列として取得できる」ということだ。
これをインタフェースとして定義するとこうなる。
========== IShortcut.cs ==========
using System;
namespace LoaferShellEx.Column {
public interface IShortcut {
string GetTarget();
}
}
========== IShortcut.cs ==========
C# ではインタフェース名の先頭に I をつける規則がある。
IShortcut という名前にしよう。
つまり、Shortcut の機能を実装するという契約だ。
インタフェースの契約書は非常にシンプルであり、
クラスと同じような記述方法で、
メソッドの宣言だけを並べて記述する。
上記の場合は、GetTarget というメソッドを 1 つ定義した。
これは前回デリゲートして定義したものに似ている。
インタフェースには IShortcut という名前があるので、
GetTarget は、契約をしたクラスの実装に対して、
リンク先の文字列を返せという意味の指示となる。
ちなみに、メソッドの特別形であるプロパティも使えるが、
ここで混ぜると話がややこしくなるので、今はやめておく。
では、昨日作った 3 クラスに、
IShortcut インタフェースを適用する。
方法は全て同じなので、ShortcutFile だけ掲載しよう。
========== ShortcutFile.cs ==========
using System;
using System.Text;
using System.Runtime.InteropServices;
using LoaferShellEx.Interop;
namespace LoaferShellEx.Column {
public class ShortcutFile : IShortcut {
private string _path;
public ShortcutFile(string path) {
_path = path;
}
public string GetTarget() {
// 省略。_path から読み込み string を返す。
}
}
}
========== end of ShortcutFile.cs ==========
見た目はほとんど変わっていない。
クラス名の後に、「: IShortcut」が付いていることで、
このクラスは IShortcut を実装していることを宣言する。
IShortcut で規定されているメソッドは GetTarget なので、
クラスでは、同じシグネチャを持つ GetTarget を実装する。
昨日の GetLinkTarget の名前を変えるだけで OK だ。
残りの InternetShortcutFile と NullShortcut も、
同じように記述を変更する。
さて、呼び出し側はどうなるか。
IShortcut shortcut = null;
switch (pscd.pwszExt.ToLower()) {
case ".lnk":
shortcut = new ShortcutFile(pscd.wszFile);
break;
case ".url":
shortcut = new InternetShortcutFile(pscd.wszFile);
break;
default:
shortcut = new NullShortcut();
break;
}
return shortcut.GetTarget();
インタフェースは、扱う側は非常に楽である。
インタフェース型は、デリゲートのように、
複数のインスタンスを保持するような複雑性はない。
クラス側ではインタフェースの実装を明示しているため、
インタフェース型に暗黙の変換を行うことができる。
そのため、インスタンスを代入するだけの単純な構文で、
インタフェースへの参照を格納することができるのだ。
そのため、呼び出し側も非常にシンプルになるのである。