こんにちは。阿部です。
C#ではクラスの継承を行う際、親クラスを一つしか指定することができません。一方で、C++など、複数クラスから継承することを許す言語もあります。では、C#で複数クラスを継承したくなったときはどうすればよいのでしょうか。
ここでは、”スプーン”クラスと”フォーク”クラスを継承して、”先割れスプーン”クラスを作る例を考えてみましょう。ちなみに、先割れスプーンは英語で”spork”と言います。”spoon + fork”で”spork”です。
スプーンとフォークは、次のようなクラスとします。
フィールドとメソッドが一つずつ定義されています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Spoon
{
public string Name = "スプーン";
public virtual void SpoonMethod()
{
Console.WriteLine(Name + "です。");
}
}
class Fork
{
public string Name = "フォーク";
public virtual void ForkMethod()
{
Console.WriteLine(Name + "です。");
}
}
|
それぞれ継承して合成する
Spoon
を継承したSpoonImpl
と、Fork
を継承したForkImpl
をそれぞれ作り、それらをメンバーとして持ったSpork
型を作ります。
ちょっと長いですが、コードは次のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
class Spork
{
// ① SpoonとForkをプロパティとして公開する
private readonly SpoonImpl _spoon = new SpoonImpl();
private readonly ForkImpl _fork = new ForkImpl();
public Spoon Spoon { get => _spoon; }
public Fork Fork { get => _fork; }
public Spork()
{
_spoon.Spork = this;
_fork.Spork = this;
}
// ② SpoonやForkからSporkに変換するメソッド
public static Spork FromSpoon(Spoon spoon) => (spoon as SpoonImpl)?.Spork;
public static Spork FromFork(Fork fork) => (fork as ForkImpl)?.Spork;
// ③ SporkをSpoonやForkに変換する演算子
public static implicit operator Spoon(Spork value) => value._spoon;
public static implicit operator Fork(Spork value) => value._fork;
// ④ SpoonとForkを継承した内部クラス
class SpoonImpl : Spoon
{
public Spork Spork;
public override void SpoonMethod()
{
Console.WriteLine("オーバーライドされた" + Name + "です。");
}
}
class ForkImpl : Fork
{
public Spork Spork;
public override void ForkMethod()
{
Console.WriteLine("オーバーライドされた" + Name + "です。");
}
}
// ⑤ 必要に応じて、SpoonとForkのメンバをクラス外に公開する(委譲)
public void SpoonMethod() => _spoon.SpoonMethod();
public void ForkMethod() => _fork.ForkMethod();
}
|
動作を確認してみましょう。
1
2
3
4
5
6
7
8
|
static void Main(string[] args)
{
var spork = new Spork();
spork.Spoon.Name = "すぷ~ん"; // Spoonのフィールドにアクセスできる
spork.Fork.Name = "ふぉ~く";// Forkのフィールドにもアクセスできる
spork.SpoonMethod();// Spoonのメソッドを実行できる
spork.ForkMethod(); // Forkのメソッドも実行できる
}
|
実行結果:
1
2
|
オーバーライドされたすぷ~んです。
オーバーライドされたふぉ~くです。
|
ちゃんとメソッドがオーバーライドされています。spork
がSpoon
としての振る舞いと、Fork
としての振る舞いをしているように見えます。
多態性
次に、多態性について見ていきます。③で、Spork
をSpoon
型やFork
型に変換する演算子を定義しています。
これによって、Spoon
やFork
が要求される場面で、Spork
を代わりに使えるようになっています。
例えば、Spork
のインスタンスをFork
のコレクションに追加することができます。
1
2
3
4
5
6
|
// SporkをForkのリストに追加できる
var forks = new List<Fork>();
forks.Add(spork);
var fork = forks[0]; // しかし、取り出すとただのForkになってしまう
// var spork1 = fork as Spork; // 残念ながら、ForkからSporkに戻せない
|
このようにSpork
からFork
への変換(アップキャスト)はできるのですが、Fork
からSpork
に戻すこと(ダウンキャスト)ができません。このあたりが限界のようです。
そこで、代替案として次のようなstatic
メソッドを定義しています。
1
|
var spork1 = Spork.FromFork(fork); // Sporkに戻すためのメソッド
|
これで、Fork
のコレクションからSpork
を取り出すことができます。
まとめ
- C#で複数クラスを継承したい場合は、個別に継承したクラスを作り、それらをメンバに持たせたクラスを作ります。(①、④)
- 委譲によって、複数クラスのメソッドを継承しているように見せられます(⑤)
- 型変換演算子によって、アップキャストを模倣することができます。(③)
- ダウンキャストの模倣はできませんでしたが、どうしても必要な場合は、子クラス側に変換用の
static
メソッドを定義しましょう。