Code contracts and inheritance
- by DigiMortal
In my last posting about code contracts I introduced you how to force code contracts to classes through interfaces. In this posting I will go step further and I will show you how code contracts work in the case of inherited classes. As a first thing let’s take a look at my interface and code contracts. [ContractClass(typeof(ProductContracts))] public interface IProduct { int Id { get; set; } string Name { get; set; } decimal Weight { get; set; } decimal Price { get; set; } } [ContractClassFor(typeof(IProduct))] internal sealed class ProductContracts : IProduct { private ProductContracts() { } int IProduct.Id { get { return default(int); } set { Contract.Requires(value > 0); } } string IProduct.Name { get { return default(string); } set { Contract.Requires(!string.IsNullOrWhiteSpace(value)); Contract.Requires(value.Length <= 25); } } decimal IProduct.Weight { get { return default(decimal); } set { Contract.Requires(value > 3); Contract.Requires(value < 100); } } decimal IProduct.Price { get { return default(decimal); } set { Contract.Requires(value > 0); Contract.Requires(value < 100); } } } And here is the product class that inherits IProduct interface. public class Product : IProduct { public int Id { get; set; } public string Name { get; set; } public virtual decimal Weight { get; set; } public decimal Price { get; set; } } if we run this code and violate the code contract set to Id we will get ContractException. public class Program { static void Main(string[] args) { var product = new Product(); product.Id = -100; } } Now let’s make Product to be abstract class and let’s define new class called Food that adds one more contract to Weight property. public class Food : Product { public override decimal Weight { get { return base.Weight; } set { Contract.Requires(value > 1); Contract.Requires(value < 10); base.Weight = value; } } } Now we should have the following rules at place for Food: weight must be greater than 1, weight must be greater than 3, weight must be less than 100, weight must be less than 10. Interesting part is what happens when we try to violate the lower and upper limits of Food weight. To see what happens let’s try to violate rules #2 and #4. Just comment one of the last lines out in the following method to test another assignment. public class Program { static void Main(string[] args) { var food = new Food(); food.Weight = 12; food.Weight = 2; } } And here are the results as pictures to see where exceptions are thrown. Click on images to see them at original size. Violation of lower limit. Violation of upper limit. As you can see for both violations we get ContractException like expected. Code contracts inheritance is powerful and at same time dangerous feature. Although you can always narrow down the conditions that come from more general classes it is possible to define impossible or conflicting contracts at different points in inheritance hierarchy.