「派生型は基本型の動作を変更してはならない」とは、基本型を使用しているかのように派生型を使用できる必要があることを意味します。たとえば、電話をかけることができる場合は、x = baseObj.DoSomeThing(123)
も呼び出すことができる必要がありますx = derivedObj.DoSomeThing(123)
。基本メソッドが例外をスローしなかった場合、派生メソッドは例外をスローしないはずです。基本クラスを使用するコードは、派生クラスでもうまく機能するはずです。別のタイプを使用していることを「確認」するべきではありません。これは、派生クラスがまったく同じことをしなければならないという意味ではありません。それは無意味でしょう。言い換えると、派生型を使用しても、基本型を使用してスムーズに実行されていたコードが破損することはありません。
例として、コンソールにメッセージを記録できるようにするロガーを宣言したと仮定しましょう。
logger.WriteLine("hello");
ログを生成する必要があるクラスでコンストラクターインジェクションを使用できます。これで、コンソールロガーを渡す代わりに、コンソールロガーから派生したファイルロガーを渡します。ファイルロガーが「メッセージ文字列に行番号を含める必要があります」という例外をスローした場合、LSPが破損します。ただし、ログがコンソールではなくファイルに送られることは問題ではありません。つまり、ロガーが呼び出し元に対して同じ動作を示した場合、すべてが正常です。
次のようなコードを作成する必要がある場合は、LSPに違反します。
if (logger is FileLogger) {
logger.Write("10 hello"); // FileLogger requires a line number
// This throws an exception!
logger.Write("hello");
} else {
logger.Write("hello");
}
ちなみに、new
キーワードはポリモーフィズムに影響を与えません。代わりに、基本タイプのメソッドと同じ名前を持っているが、それに関連していない完全に新しいメソッドを宣言します。特に、ベースタイプで呼び出すことはできません。ポリモーフィズムが機能するには、override
キーワードを使用する必要があり、メソッドは仮想である必要があります(インターフェイスを実装している場合を除く)。