"The derived types must not change the behavior of the base types" means that it must be possible to use a derived type as if you were using the base type. For instance, if you are able to call x = baseObj.DoSomeThing(123)
you also must be able to call x = derivedObj.DoSomeThing(123)
. The derived method should not throw an exception if the base method didn't. A code using the base class should be able to work well with the derived class as well. It should not "see" that it is using another type. This does not mean that the derived class has to do exactly the same thing; that would be pointless. In other words using a derived type should not break the code that was running smoothly using the base type.
As an example let's assume that you declared a logger enabling you to log a message to the console
logger.WriteLine("hello");
You could use constructor injection in a class needing to produce logs. Now instead of passing it the console logger you pass it a file logger derived from the console logger. If the file logger throws an exception saying "You must include a line number in the message string", this would break LSP. However, it is not a problem that the logging goes to a file instead of the console. I.e. if the logger shows the same behavior to the caller, everything is okay.
If you need to write a code like the following one, then LSP would be violated:
if (logger is FileLogger) {
logger.Write("10 hello"); // FileLogger requires a line number
// This throws an exception!
logger.Write("hello");
} else {
logger.Write("hello");
}
By the way: The new
keyword does not influence polymorphism, instead it declares a completely new method that happens to have the same name as a method in the base type but is not related to it. In particular, it is not possible to call it through a base type. For polymorphism to work, you must use the override
keyword and the method must be virtual (unless you are implementing an interface).