質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

1591閲覧

c#でオーバライドしたメソッドの呼び出しが思惑通りにいきません。

anosuteki

総合スコア11

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2016/11/30 14:39

以下のコードを実行すると、

不明な飲み物
1.09

と出力されますが、本当は、

ダークローストコーヒー、ホイップ
1.09

と出力されてほしいです。

何がいけないのか、どうすれば思惑通りにいくか、教えていただけないでしょうか。

説明不足でしたらすみません。
コードも冗長で申し訳ありませんが、なにとぞお願いいたします。

###該当のソースコード

C#

1using System; 2abstract class Beverage { 3 protected string description = "不明な飲み物"; 4 5 public virtual string GetDescription() { 6 return description; 7 } 8 9 public abstract double Cost(); 10} 11 12 13abstract class Topping : Beverage { 14 public abstract string GetDescription(); 15} 16 17 18class Whip : Topping { 19 Beverage beverage; 20 21 public Whip(Beverage beverage) { 22 this.beverage = beverage; 23 } 24 25 public override string GetDescription() { 26 return beverage.GetDescription() + "、ホイップ"; 27 } 28 29 public override double Cost() { 30 return beverage.Cost() + 0.30; 31 } 32} 33 34class DarkRoast : Beverage { 35 public DarkRoast() { 36 description = "ダークローストコーヒー"; 37 } 38 39 public override double Cost() { 40 return 0.79; 41 } 42} 43 44interface Test { 45 void Start(); 46} 47 48class TestDecoratorPattern : Test { 49 public void Start() { 50 Beverage beverage = new DarkRoast(); 51 beverage = new Whip(beverage); 52 53 Console.WriteLine(beverage.GetDescription()); 54 Console.WriteLine(beverage.Cost()); 55 56 } 57} 58 59class Program { 60 static void Main() { 61 Test test_decorator = new TestDecoratorPattern(); 62 test_decorator.Start(); 63 } 64}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

#原因
ToppingクラスがBeverageクラスのGetDescriptionを隠蔽してしまっているのが、一見不可解な現象の原因です。

C#

1abstract class Topping : Beverage { 2 //下のようにoverrideせずに基底クラスと被る関数を宣言するのは「隠蔽」という 3 public abstract string GetDescription(); 4}

GetDescription() が隠蔽されるとどうなるかというと、呼び出し時に格納されている変数の型だとかキャストの有無だとかの繊細な違いで挙動が変わってしまうようになります。

C#

1public void Start2() 2 { 3 Beverage beverage = new DarkRoast(); 4 beverage = new Whip(beverage); 5 6 //これは「不明な飲み物」と出力されるが、 7 Console.WriteLine(beverage.GetDescription()); 8 9 //これは「ダークローストコーヒー、ホイップ」と出力される 10 Console.WriteLine(((Topping)beverage).GetDescription()); 11 }

このように、隠蔽は悩ましいバグの元になるので、特別な理由がない限りは使用を避けたほうが良いです。

ちなみにc#には、隠蔽をわざと使っているのだということを明確に示すためのnewというキーワードがわざわざ用意されています。

これがあるおかげで、VisualStudioは間違って隠蔽を使っている可能性のある箇所(隠蔽してるのにnewがついてない箇所)に親切で警告を出してくれます。

anosutekiさんの提示のコードでも、以下のような警告が出ませんでしたか?

イメージ説明

この警告は、意訳すると概ね次のようなことを言っています。

「継承されたメソッドと同じメソッドが宣言されているので隠蔽として解釈されてしまいます。オーバーライドのつもりならoverrideキーワードを付けて実装を書いてください。隠蔽で合ってるなら、うっかりミスじゃないことを示すためにnewキーワードをつけて安心させて下さい。」

#問題を解消するには

  • Toppingクラス内のGetDescription()の宣言を消す

警告は「GetDescriptionを書くならoverrideするかnewをつけろ」と言っていますが、今回はオーバーライドしたいわけでも隠蔽したいわけでもなく単純に基底の実装を受け継ぎたいので、そもそもGetDescriptionをToppingクラス内に書かないのが正解です。

C#

1abstract class Topping : Beverage { 2}

投稿2016/11/30 17:27

MagoCat

総合スコア86

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

anosuteki

2016/12/01 12:20

非常に親切で丁寧なご回答、ありがとうございます。 ToppingクラスからGetDescriptionを取ってあげることで解決できました。 文中の警告も確かに表示されていました。 隠ぺいについての理解が足りていないことがわかりましたので、 勉強してみます。
guest

0

Topping クラスは Bevarage クラスを継承しているので GetDescription を定義する必要はありません。この一行を削除すれば、思惑通り正しく動作しますね!

この挙動は少しわかりにくいですが、Whip クラスの GetDescriptionTopping クラスの GetDescription をオーバーライドしているので、Start メソッドの beverage.GetDescription() が呼び出している Beverage クラスの GetDescription をオーバーライドできていないのです。

投稿2016/11/30 15:13

chitoku

総合スコア1610

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

anosuteki

2016/12/01 12:21

ご回答ありがとうございます。 GetDescriptionを外すことで思惑通りの動きになりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問