以前
オブジェクト指向プログラミング入門のこのボーナスラウンドを通じて、私たちは旅の終わりに近づいています!前回の記事では、クラス、オブジェクト、およびメソッドのレビューからふれあい動物園の例を開始しました。このシリーズの冒頭を見逃した場合は、ここで私たちに追いつくことができます。それ以外の場合は、戻ってみましょう!
。
建設業者
2つのDog
を使用して演習を行った場合 オブジェクト、それは少し退屈でしたね?結局のところ、どの犬がどの樹皮を生産したかというソースコードを見ずに、犬を互いに分離することはできず、知る方法もありません。
前回の記事で、オブジェクトを作成するときに、コンストラクターと呼ばれる特別なメソッドを呼び出すことを説明しました。 。コンストラクターは、メソッドとして記述されたクラス名のように見えます。たとえば、Dog
の場合 クラスの場合、コンストラクターはDog()
と呼ばれます。 。
コンストラクターの特別な点は、コンストラクターが新しいオブジェクトへのパスであるため、オブジェクトをデフォルト値で初期化するコードを呼び出すのに最適な場所です。さらに、コンストラクターメソッドからの戻り値は常にクラス自体のオブジェクトであるため、作成するクラスのタイプの変数にコンストラクターの戻り値を割り当てることができます。
ただし、これまでのところ、実際にはコンストラクターをまったく作成していないので、なぜそのメソッドを呼び出すことができるのでしょうか。
C#を含む多くの言語では、この言語は何もしなくても無料で空のコンストラクターを提供します。コンストラクターが必要であることを意味します。そうでなければ、クラスを何かに使用する方法がないので、言語はあなたがクラスを書いたと仮定するだけです。
この目に見えない無料のコンストラクターは、デフォルトコンストラクターと呼ばれます。 、そして、この例では、次のようになります。
public Dog(){ }
この構文はSpeak()
と非常によく似ていることに注意してください。 以前に作成したメソッド。ただし、値を明示的に返さず、メソッドの戻り型を宣言することもありません。前述したように、コンストラクターは常に、それが属するクラスのインスタンスを返します。
この場合、それはクラスDog
、そのため、Dog myDog = new Dog()
を記述します。 、 myDogという名前の変数に新しいオブジェクトを割り当てることができます タイプはDog
。
それでは、デフォルトのコンストラクターをDog
に追加しましょう。 クラス。上記の行をコピーするか、VisualStudioでショートカットを使用できます。ctor
と入力します。 Tab
を押します 2回。図9に示すように、デフォルトのコンストラクターが生成されます。
図9:「ctor」を使用したコンストラクターの追加
デフォルトのコンストラクターは、以前は暗黙的に行われていたことを明示的に実行しているため、実際には何も新しいものを提供しません。ただし、これはメソッドであるため、このコンストラクターを呼び出すたびに実行されるコンテンツを角かっこ内に追加できるようになりました。また、コンストラクターはオブジェクトの構築の最初のものとして実行されるため、初期化コードを追加するのに最適な場所です。
たとえば、Name
を設定できます 次のようなコードを追加して、オブジェクトのプロパティを何かに追加します。
public Dog() { this.Name = "Snoopy"; }
この例では、Name
を設定します 「スヌーピー」への新しいオブジェクトのプロパティ。
もちろん、すべての犬が「スヌーピー」と呼ばれるわけではないため、これはあまり役に立ちません。代わりに、コンストラクターのメソッドシグネチャを変更して、パラメーターを受け入れるようにします。
メソッドの括弧は、見栄えを良くするためだけのものではありません。これらは、メソッドに値を渡すために使用できるパラメーターを含むのに役立ちます。この関数は、コンストラクターだけでなく、すべてのメソッドに適用されますが、最初にコンストラクターに対して実行しましょう。
デフォルトのコンストラクター署名を次のように変更します:
public Dog(string dogName)
この追加により、string
を送信できます パラメーターをコンストラクターに追加します。そうすると、そのパラメーターをdogName
という名前で参照できます。 。
次に、メソッドブロックに次の行を追加します。
this.Name = dogName;
この行は、このオブジェクトのプロパティName
を設定します コンストラクターに送信したパラメーターに。
コンストラクターの署名を変更すると、図10に示すように、Program.csファイルに赤い波線のケースが表示されることに注意してください。
図10:新しいコンストラクターからの赤い波線のケース
独自の明示的なコンストラクターを追加する場合、C#と.NETはデフォルトのコンストラクターを暗黙的に作成しません。 Program.csファイルでは、まだDog
を作成しています デフォルトのパラメーターなしのコンストラクターを使用するオブジェクト。現在は存在しません。
この問題を修正するには、Program.csのコンストラクター呼び出しにパラメーターを追加する必要があります。たとえば、オブジェクト下書き線を次のように更新できます。
Dog myDog =new Dog(“ Snoopy”);
そうすることで、赤い波線が削除され、コードを再度実行できるようになります。最後のコード行の後にブレークポイントを残すか設定する場合は、[ローカル]パネルを見て、オブジェクトのName
を確認できます。 図11に示すように、プロパティは実際に設定されています。
図11:Nameプロパティが正しく設定されていることを確認する
とった?良い!犬に名前を付けることができるようになりましたが、犬をデバッグして私たちが何をしているかを確認する必要がある場合、これは実際には便利なプログラムではありません。それでは、コードを少し混ぜて、吠えている犬の名前が表示されるようにします。
Dog
を更新します オブジェクトを変更し、Speak()
を変更します そのような方法:
public void Speak() { Console.WriteLine(this.Name + " says: Woof"); }
これで行った変更により、WriteLine
が通知されます。 このオブジェクトの名前をリテラル文字列「says:Woof」と連結するメソッド。これにより、作業をより適切に表示する出力が得られるはずです。今すぐプログラムを実行してみると、図12のようなものが表示されるはずです。
図12:スヌーピーのコメント:Woof
よくやった!これで、異なる名前の犬をたくさん作成することができ、それらはすべてあなたの命令で吠えます。
もちろん、犬だけのふれあい動物園はやや退屈です。つまり、私は犬が大好きですが、体験を少し盛り上げるために動物を追加することもできますか?
。
継承
新しいCat
を追加することから始めましょう 私たちのAnimals.csファイルへのクラス。 Dogクラスの横に、ただしPettingZoo名前空間ブラケット内に次のコードを追加します。
class Cat { public Cat(string catName) { this.Name = catName; } string Name; public void Speak() { Console.WriteLine(this.Name + " says: Meow!"); } }
次に、Cat
を作成しましょう Program.csのオブジェクトを作成し、話してもらいます:
Cat myCat = new Cat("Garfield"); myCat.Speak();
このプログラムを今すぐ実行すると、図13のような出力が得られます。
図13:ガーフィールドのコメント:ニャー
私たちのプログラムは予測可能で機能しますが、2つのクラスが実際にどれほど似ているかに気づきましたか? 2つのクラス間で複製したコードの量を確認してください。オブジェクト指向は、コードを何度も書くことから私たちを救うはずではありませんでしたか?
答えはイエスです。このコードを単純化して、重複の量を減らすことができます。これから説明するのは、継承と呼ばれるOOの機能を示します。 。
オブジェクト指向の継承により、クラスの階層を作成し、各クラスに親クラスのプロパティとメソッドを継承させることができます。たとえば、ふれあい動物園の場合、親のAnimal
を作成できます。 クラスとDog
があります およびCat
そのクラスから継承します。次に、コードの一部をAnimal
に移動できます。 両方のDog
が およびCat
クラスはそのコードを継承します。
ただし、コードの重複部分をこのAnimal
に移動した場合 クラス、次にDog
およびCat
クラスはそれぞれ同じプロパティとメソッドを継承し、子を親クラスのクローンにします。この組織は、異なる種類の動物が必要なため、あまり役に立ちません。猫、犬、そして他のすべての動物が同じだったら、それはひどく退屈でしょう。実際、私たちのプログラムの目的上、それらを別のものと呼ぶことはまったく意味がありません。
さて、継承によって、親クラスと同一の子クラスしか作成できない場合、このすべての作業に取り組む意味はあまりありません。したがって、親クラスのすべての部分を継承する一方で、子クラスの親クラスの特定の部分をオーバーライドして、子クラスを区別することもできます。
これを見て、どのように機能するか見てみましょう。
。
親と子のクラスの作成
少し前に戻って、Dog
を再作成します。 およびCat
より良い方法でクラス。まず、子クラスの共有プロパティとメソッドを定義する親クラスが必要です。
Animals.csファイルで、Dog
を削除します およびCat
クラスを追加し、次のクラス定義を追加します。
class Animal { public string Name; public string Sound; public void Speak() { Console.WriteLine(this.Name + " says " + this.Sound); } }
このクラスは、当然のことながら、以前のDog
と同じように見えます。 およびCat
すべてのプロパティとメソッドを公開し、 Sound という新しいプロパティを導入したことを除いて、クラス。 タイプstring
。最後に、Speak
を更新しました
この時点で、Program.csは機能しません。そのタブをクリックすると、いくつかのエラーメッセージが表示されます。これは、Cat
を削除したため当然です。 およびDog
クラス。それらを再作成し、継承を使用して再作成しましょう。 Animals.csファイルで、Animal
の直後 クラスには、次のコードを追加します:
class Dog : Animal { } class Cat : Animal { }
これらの新しいクラスは以前よりもはるかに短く、コードを複製しません。ここでの新しい構文は、コロンの後にクラス名Animal
が続くものです。 、C#にDog
の両方が必要であることを通知します およびCat
Animal
から継承する クラス。事実上、Dog
およびCat
Animal
の子クラスになります 図14に示す図に示すように。
図14:動物クラスの継承の図
注:私はプロパティという用語を使用しています これまでのところ、正しい用語は field であるため、少し不正確です。 図14の図からわかるように、フィールドはプログラミングの範囲外ではあまり明確ではないため、代わりにプロパティを使用しました。技術的には、プロパティはC#のフィールドのラッパーです。
。
ただし、作成したカスタムコンストラクターが原因で、Program.csファイルにまだ問題があります。プログラムを実行またはデバッグしようとすると、「「PettingZoo.Cat」には1つの引数を取るコンストラクターが含まれていません」というエラーメッセージと、「PettingZoo.Dog」の同様のコンストラクターが表示されます。
このエラーを修正するには、コンストラクターを再度追加する必要があります。ただし、今回は、継承を利用してコンストラクターを継承し、コンストラクターを拡張して親クラスの機能を変更する方法を示します。
まず、Animal
のベースコンストラクターを作成する必要があります 、これは以前に作成した以前のコンストラクターに似ています。次のコードをAnimal
に追加します クラス:
public Animal(string animalName) { this.Name = animalName; }
前と同じように、コンストラクターは動物の名前を渡したものに設定します。ただし、両方のDog
にコンストラクターを追加する必要があるため、これだけでは不十分です。 およびCat
クラス。これを使用して、各動物がどの音を出すかを定義します。
class Dog : Animal { public Dog(string dogName) : base(dogName) { this.Sound = "Woof"; } } class Cat : Animal { public Cat(string catName) : base(catName) { this.Sound = "Meow"; } }
どちらのクラスにも、Name
を受け入れるコンストラクターを追加します パラメータ。このオブジェクトのSound
も設定します 動物に作ってもらいたい音の特性。
ただし、以前とは異なり、親クラスコンストラクターを呼び出してName
を渡すようになりました。 親コンストラクターからのパラメーター。この呼び出しは、: base()
を介して行われます。 メソッドを使用し、受信したdogName
を追加します またはcatName
括弧内のパラメータ。
注:この
: base()
構文はコンストラクターに固有です。ケースの機能を変更または拡張する他の方法については、後で説明します。
。
これらのコードの更新により、プログラムを再度実行して、図15のような結果を確認できます。
図15:継承を使用して話す動物
Sound
もありません。 Name
でもありません Dog
のいずれかで定義されたプロパティ またはCat
クラス。 Speak()
もありません 方法。代わりに、それらは親のAnimal
に存在します クラス、およびDog
およびCat
クラスはこれらのプロパティとメソッドを継承します。
同様に、他のクラスを作成できます。どのタイプの動物でも、Dog
のパターンを複製するだけで済みます。 およびCat
クラス。たとえば、次のクラスを作成できます。
class Parrot : Animal { public Parrot(string parrotName) : base(parrotName) { this.Sound = "I want a cracker!"; } } class Pig : Animal { public Pig(string pigName) : base(pigName) { this.Sound = "Oink"; } }
次に、Program.csでこれらのクラスをインスタンス化できます:
Parrot myParrot = new Parrot("Polly"); myParrot.Speak(); Pig myPig = new Pig("Bacon"); myPig.Speak();
これで、図16に示すように、プログラムを実行すると、真の動物園が手に入ります。
図16:話す4匹の動物の動物園
既存の子クラスのサブ子クラスをさらに作成することもできます。たとえば、Dog
を分離することができます Beagle
にクラス分けする およびPointer
またはParrot
クラスは親から継承しますBird
次に、Animal
から継承するクラス 。ただし、この種の階層を作成することはこの記事の範囲を超えているため、次に進んで、最後に、子クラスの親クラスの機能をオーバーライド、変更、または拡張する方法を見てみましょう。
。
継承の変更
オーバーライドと呼ばれる手法を使用して、親クラスのメソッドを変更できます。 。オーバーライドの構文は言語によって異なる場合がありますが、子クラスで親のメソッドを変更する原則は同じです。
C#では、キーワードoverride
を追加します その後に、オーバーライドするメソッドシグネチャが続きます。また、親クラスで、virtual
を追加して子がメソッドをオーバーライドできるようにすることを宣言する必要があります。 親クラスのメソッドシグネチャへのキーワード。 (他の言語では、この追加の表示は必要ない場合があります。)
このプログラムでは、Speak()
を更新する必要があります Animal
のメソッド クラス。
public virtual void Speak() { Console.WriteLine(this.Name + " says " + this.Sound); }
これで、Speak()
のオーバーライドを挿入できます。 Dog
のメソッド このクラスのコードブロックの先頭にあるクラス。
class Dog : Animal { public override void Speak() { base.Speak(); Console.WriteLine("...and then runs around, chasing his tail"); } [remaining code omitted]
このコードは、親メソッドSpeak()
で発生することをオーバーライドすることを示しています。 。 base.Speak()
を使用して親メソッドを実行します 直後に追加の行を出力する前に呼び出します。
なぜあなたはそうしたいのですか?ええと、おそらく私達は私達の犬が彼らが直接会うことができるのと同じくらい熱狂的であることを望みます。プログラムを実行して、自分の目で確かめてください:
図17:私たちの犬は彼のクラスメソッドをオーバーライドします
まず、スヌーピーは通常どおり吠えます:base.Speak()
メソッドはSpeak()
を呼び出します 親のAnimal
からのメソッド クラス。次に、残りのコードは2行目をコンソールに出力します。事実上、子クラスにのみ新しい機能を追加することにより、親クラスを拡張します。 Cat
の様子をご覧ください クラスは影響を受けません。
もちろん、猫は思い通りに行動するのが難しいことで有名なので、それをコードに反映させましょう。 Cat
を更新します クラスを作成し、Dog
と同様に、次のメソッドオーバーライドを追加します クラス。
class Cat : Animal { public override void Speak() { Console.WriteLine(Name + " doesn't speak but just sits there wondering when you will feed it."); } [remaining code omitted]
Dog
で行ったこととは異なり オーバーライドします。base.Speak()
は呼び出しません。 今回はメソッド。その呼び出しがなければ、Animal
を実行することはありません。 Speak()
メソッドとこのメソッドで何が起こるかを完全に変更します。自分の目で確かめてください:
図18:猫もクラスメソッドをオーバーライドします
また、クラスを再構築したため、Program.csコードを変更していないことにも注意してください。これらの変更は、オブジェクト指向が非常に強力な手法である理由の良い例として役立ちます。そのコードを使用するプログラムに触れることなく、基礎となるコードを完全に変更しました。 [オブジェクト指向により、実装の詳細の多くを分離し、プログラムが対話する必要のあるインターフェイスの小さなセットを公開することができました。]
ルールを曲げた場所
まとめる前に、いくつかのこと、主に私たちが行った「悪い」ことを指摘したいと思います。
まず、クラスでは、Console.WriteLine()
を呼び出します。 直接メソッド。クラスはクラスの結果を出力する方法に依存しないため、この使用法は適切な設計上の決定ではありません。
より良いアプローチは、代わりにクラスからデータを返し、それをプログラムの一部として出力することです。次に、プログラムがクラスではなく出力をどう処理するかを決定できるようにし、Webページ、Windowsフォーム、QT、コンソールアプリケーションなど、さまざまな種類のプログラムで同じクラスを使用できるようにします。
次に、後の例では、すべてのプロパティがpublic
でした。 。これらのプロパティを公開すると、オブジェクト指向のセキュリティ面が弱まります。オブジェクト指向では、クラス内のデータを外部からの操作から保護しようとします。ただし、継承を処理する場合、プロパティとメソッドの保護はすぐに複雑になるため、少なくともこの導入段階では、その複雑さを避けたかったのです。
最後に、Sound
を使用しても意味がありません。 そのコードも複製していたので、子コンストラクターの一部として設定しました。名前とサウンドの両方を受け入れる親クラスにコンストラクターを作成し、それを子コンストラクターの一部として呼び出す方がよい設計です。ただし、簡単にするために、この段階では異なるコンストラクター署名を導入したくありませんでした。
したがって、このボーナスチュートリアルでは多くのことを学びましたが、さらに学ぶべきことがあることがわかります。ただし、まだあまり心配する必要はありません。ずっと続いたことを祝福するために少し時間をとることができます。すべての手順を実行した場合は、次のことを学習しました。
- オブジェクト指向を使用する理由
- クラスの作成方法
- プロパティとメソッドを作成する方法
- コンストラクターの作成方法とその機能
- 継承を利用する方法と、それがオブジェクト指向の強力な機能である理由
この追加のレッスンを楽しんでいただき、さらに探索することに興味を持っていただければ幸いです。 Atlantic.Netブログで新しいコンテンツを確認し、業界をリードする仮想プライベートホスティングサーバーの1つを検討してください。
。