抽象メソッドとは、実装を持たず、メソッドの意味だけを定義したメソッドです。 抽象メソッドの実装は基底クラスでは行わず、派生クラスで行います。
また、抽象クラスとは、 インスタンスを生成出来ないクラスのことで、 継承を行うことを前提としたクラスのことです。
「多態性」
で、
仮想メソッドの利用例として Person クラスを挙げました。
この Person 基底クラスには、
Age というプロパティがありますが、
このプロパティ自体は意味のある値を返さず、
実装は派生クラスの Age プロパティで行っていました。
class Person { // ここではあんまり関係ないんで name は省略。 protected int age; public Person(int age){this.age = age;} public virtual int Age { // 基底クラスでは特に意味のない値を返す。 // 意味のある実装は派生クラスで行います。 get { return 0; } } }
しかし、Person クラスのように、
意味のない値を返すメソッドを持つクラスのインスタンスが生成されてしまうというのはあまり好ましいことではありません。
この問題を解決するためには2つの方法があります。
1つは基底クラスにデフォルトの動作を定める方法です。
すなわち、
性善説を信じて Person がデフォルトで正直な答えを返すようにするか、
性悪説を信じて Person がデフォルトで鯖を読むようにするか、
とにかく、Person の Age プロパティが何らかの意味を持つ値を返すようにします。
class Person { protected int age; public Person(int age){this.age = age;} public virtual int Age { // 性善説を信じてみる。 // 普通の人はみんな正直に年齢を答えてくれるに違いない。 get { return this.age; } } }
そして、もう1つの方法は、Person クラスのインスタンスを生成出来ないようにすることです。
例えば、Person クラスのコンストラクタを protected にしてしまえば、Person クラスのインスタンスは外部から生成できなくなります。
class Person { protected int age; // ↓ protected なので外部からコンストラクタを呼べない。 // Person は継承して使う専用のクラスになります。 protected Person(int age){this.age = age;} public virtual int Age{get{return 0;}} }
これで Person クラスのインスタンスが作られることはなくなるんですが、
まだ Person クラスに意味のないメソッドの実装が残っています。
これは意味のないものをわざわざ書かなくてはいけないので無駄になりますし、
サブクラスでちゃんとオーバーロードしなければ無意味な値が返されてしまうという問題があります。
この問題を解決するため、 C# にはインスタンスを作成できないクラスや、 実装のない(派生クラスで必ずオーバーロードしなければならない)メソッドを定義するための構文が用意されています。
インスタンスを作成できないクラスは抽象クラス(abstract class)と呼ばれています。
抽象クラスを作成するには、クラスの定義時に abstract 修飾子を付けます。
abstract class Person { protected int age; // 抽象クラスなので、コンストラクタが public であってもインスタンスは生成できない。 public Person(int age){this.age = age;} public virtual int Age{get{return 0;}} }
また、実体を持たず、意味だけを定義し、実装は派生クラスで行うメソッドは抽象メソッド(abstract method)と呼ばれています。
抽象メソッドを作成するには、メソッドの定義時に abstract 修飾子を付けます。
抽象メソッドは抽象クラス中でしか定義できません。
abstract class Person { protected int age; public Person(int age){this.age = age;} public abstract int Age{get;} // 抽象メソッドには定義は要らない }
いままで例に挙げてきた Person クラスの最終形です。
using System; abstract class Person { protected string name; protected int age; public Person(string name, int age) { this.name = name; this.age = age; } public string Name{get{return this.name;}} public abstract int Age{get;} // 抽象メソッドには定義は要らない } /// <summary> /// 正直者。 /// 年齢を偽らない。 /// </summary> class Truepenny : Person { public Truepenny(string name, int age) : base(name, age){} public override int Age { get { // 実年齢をそのまま返す。 return this.age; } } } /// <summary> /// 嘘つき。 /// 鯖を読む(しかも、歳取るにつれ大幅に)。 /// </summary> class Liar : Person { public Liar(string name, int age) : base(name, age){} public override int Age { get { // 年齢を偽る。 if(this.age < 20) return this.age; if(this.age < 25) return this.age - 1; if(this.age < 30) return this.age - 2; if(this.age < 35) return this.age - 3; if(this.age < 40) return this.age - 4; return this.age - 5; } } } /// <summary> /// いいかげん。 /// 大体の歳しか答えない。 /// </summary> class Equivocator : Person { public Equivocator(string name, int age) : base(name, age){} public override int Age { get { // 年齢を四捨五入した値を返す。 return ((this.age + 5) / 10) * 10; } } } class PolymorphismTest { public static void Main() { Introduce(new Truepenny ("Ky Kiske" , 24)); //正直者のカイさん24歳。 Introduce(new Liar ("Axl Low" , 24)); //嘘つきのアクセルさん24歳。 Introduce(new Equivocator("Sol Badguy", 24)); //いい加減なソルさん24歳。 } /// <summary> /// p さんの自己紹介をする。 /// </summary> static void Introduce(Person p) { Console.Write("My name is {0}.\n", p.Name); Console.Write("I'm {0} years old.\n\n", p.Age); } }
My name is Ky Kiske. I'm 24 years old. My name is Axl Low. I'm 23 years old. My name is Sol Badguy. I'm 20 years old.