Liskov substitution principle (LSP) says:
Preconditions cannot be strengthened in a subtype.
In C#, I could violate the whole principle as follows:
public class A
{
public virtual void DoStuff(string text)
{
Contract.Requires(!string.IsNullOrEmpty(text));
}
}
public class B : A
{
public override void DoStuff(string text)
{
Contract.Requires(!string.IsNullOrEmpty(text) && text.Length > 10);
}
}
But, what would happen if A.DoStuff
would be an abstract
method:
public class A
{
public abstract void DoStuff(string text);
}
public class B : A
{
public override void DoStuff(string text)
{
Contract.Requires(!string.IsNullOrEmpty(text));
}
}
Now A.DoStuff
is contractless. Or its contract is just everything allowed.
So, is B.DoStuff
precondition violating Liskov substitution principle?
It depends on what defines the contract.
The LSP is a theoretical construct, it does not depend on a specific language or implementation, such as C#'s "Code Contracts" feature.
The contract can be defined by:
- the method name
- the method parameter names
- the method comment
- the return type and the method parameter types
- "explicit" contracts such as
Contract.Requires
On the last two will be verified by the compiler. However, the first three can be part of the contract as well! Consider the following example:
public interface StuffContainer
{
void Add(string text);
// Removes a string that has previously been added.
void Remove(string text);
}
The name and the documentation of the Remove
method define a clear precondition. Verifying in an implementation that the string to be removed has previously been added does not violate the LSP. Verifying that the string has at least 5 characters would violate the LSP.
Yes, you can break the principle very easily, not only in C#.
It only states:
Subtype Requirement: Let phi(x) be a
property provable about objects x of type T. Then
phi(y) should be true for
objects y of type S where S is a subtype of T.
In your example the type B does not fulfill the property of offering a method DoStuff
that works with short texts, despite its supertype A fulfilling it. So the principle is violated.
It's up to the programmer to uphold the principle. A property could also be "it does the right thing", which you could easily break by having a subtype with a wrong implementation of a method.
I'd argue that it's not. An abstract method by definition has no preconditions because there is no implementation. It would be the same as arguing if implementing an interface breaks LSP.
Saying A.DoSomething()
is contracless is an untrue premise. A.DoSomehing()
is undefined, therefore it can not have a contract further than its signature.