Start learning free

LESSON 562 OF 572 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Shipping Options in C#: Inheritance and Overriding

Practice O(1) Read the full explanation

An online shop offers three ways to ship a parcel. Standard shipping is the basic one. Express and Economy are kinds of shipping too: they answer the same two questions, what the option is called and what it costs, but each in its own way. So each one overrides the parent's methods with its own versions.

Implement the class Shipping, the standard option:

  • name() returns "Standard".
  • cost(weight) returns 5 + weight: a base price of 5, plus 1 for each kilogram.

and two classes that inherit from Shipping and override both methods:

  • Express: name() returns "Express", and cost(weight) returns 10 + 2 * weight.
  • Economy: name() returns "Economy", and cost(weight) returns 3 plus half the weight, rounded down to a whole number: half of 7 is 3.

The input lists parcels, one per line: the option (standard, express or economy) and the weight. For each line, the code that tests your classes creates that option and prints <name>: <cost> from its name() and cost(weight). The two calls are the same for every option (where variables have types, through a variable of the parent type Shipping); because the methods are overridden, each option answers with its own version. The starter code spells these names the way your language does.

 

Example 1:

Input:
    standard 4
    express 4
    economy 4
Output:
    Standard: 9
    Express: 18
    Economy: 5
Explanation: The same parcel of 4 kilograms costs 5 + 4 = 9 with Standard, 10 + 2 × 4 = 18 with Express
and 3 + 2 = 5 with Economy (half of 4 is 2).

Example 2:

Input:
    standard 0
    express 0
    economy 0
Output:
    Standard: 5
    Express: 10
    Economy: 3
Explanation: With a weight of 0, only the base prices are left.

Example 3:

Input:
    economy 7
    economy 1
    standard 7
    express 7
Output:
    Economy: 6
    Economy: 3
    Standard: 12
    Express: 24
Explanation: Economy halves the weight and rounds down: half of 7 is 3, so 3 + 3 = 6, and half of 1 is 0, so 3 + 0 = 3.

 

Constraints:

  • 0 <= weight <= 1000, a whole number of kilograms.
  • At most 100 parcels per test case.
STEP 1 OF 6
    Ctrl + Enter to check

    C# · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Shipping Options in C#, explained

    Problem overview

    A shop ships parcels in three ways. Every way answers the same two questions: what is it called, and what does it cost for a parcel of a given weight? Only the answers differ. Standard is called "Standard" and costs 5 + weight. Express is called "Express" and costs 10 + 2 * weight. Economy is called "Economy" and costs 3 + weight / 2, where the division drops the remainder.

    You write three classes: Shipping for the standard option, and Express and Economy, which inherit from it and give their own versions of Name and Cost. A parcel of 4 kilograms costs 9, 18 and 5 with the three options.

    Approach: one parent, two overrides

    Without inheritance you would write three unrelated classes and the code using them would need to know which one it holds. With inheritance, Express and Economy are kinds of Shipping. A variable of type Shipping can hold any of the three, and calling Name() or Cost(weight) on it runs the version that belongs to the object actually stored there.

    In C# this needs two keywords that work as a pair:

    • virtual on the parent's method says "a child class may replace this";
    • override on the child's method says "this is the replacement", with exactly the same name, parameters and return type.

    A child class is declared with : Shipping after its name. It inherits everything that is accessible in the parent, and only has to write what is different.

    Step by step

    1. Write Shipping.Name so that it returns "Standard". Its header is public virtual string Name().
    2. Write Shipping.Cost so that it returns 5 + weight: a base price plus one unit per kilogram.
    3. In Express, return "Express" from Name. The header is the same as the parent's, with override in place of virtual.
    4. In Express.Cost, return 10 + 2 * weight.
    5. In Economy.Name, return "Economy".
    6. In Economy.Cost, return 3 + weight / 2. Both operands are int, so the division drops the remainder: 7 / 2 is 3.

    The solution in C#

    class Shipping {
        public virtual string Name() {
            return "Standard";
        }
    
        public virtual int Cost(int weight) {
            return 5 + weight;
        }
    }
    
    class Express : Shipping {
        public override string Name() {
            return "Express";
        }
    
        public override int Cost(int weight) {
            return 10 + 2 * weight;
        }
    }
    
    class Economy : Shipping {
        public override string Name() {
            return "Economy";
        }
    
        public override int Cost(int weight) {
            return 3 + weight / 2;
        }
    }

    The classes have no fields: the price depends only on the weight passed in, so an object needs no data of its own. That is why each class is just two small methods.

    Using the classes

    This code builds one object of each kind, stores them all in an array of the parent type and asks each one the same two questions for a 7 kilogram parcel:

    Shipping[] options = { new Shipping(), new Express(), new Economy() };
    foreach (Shipping s in options) {
        Console.WriteLine(s.Name() + ": " + s.Cost(7));
    }
    Standard: 12
    Express: 24
    Economy: 6

    The loop never checks which class it has. The same two calls give three different answers, because each object runs its own overridden version. With a weight of 0 only the base prices remain: 5, 10 and 3. With a weight of 1, Economy costs 3, since 1 / 2 is 0.

    Adding a fourth option

    Because the code that uses shipping options only knows the parent type, a new option is just one more class. This one, Overnight, reuses the parent's price with base.Cost(weight) and adds 20:

    class Overnight : Shipping {
        public override string Name() { return "Overnight"; }
        public override int Cost(int weight) { return base.Cost(weight) + 20; }
    }

    Added to the same array as the other three, with a 4 kilogram parcel, it prints:

    Standard: 9
    Express: 18
    Economy: 5
    Overnight: 29

    base means "the parent's version", which is handy when the child only wants to adjust the parent's answer. The loop that prints these lines did not change at all. That is the benefit of overriding: the code that uses the options stays closed to changes, while the list of options stays open.

    Common mistakes

    • Forgetting virtual or override. If the child's method has the same name but no override, and the parent's is not virtual, the child's method hides the parent's instead of replacing it. The compiler warns (CS0108), and a call through a Shipping variable still runs the parent's version. For example, a Child object kept in a Base variable answered "Standard", while the same object kept in a Child variable answered "Express".
    • Changing the header. An override must match the parent's method exactly: a different return type or parameter list is a different method.
    • Rounding Economy's weight the wrong way. The cost is 3 + weight / 2 with integer division. Writing it with double would produce fractions such as 6.5, which this lesson does not want.
    • Mixing up the prices. Each child replaces the whole formula: Express does not add its 10 on top of the parent's 5.

    Key takeaways

    • A child class declared with : Parent inherits the parent's members.
    • Mark the parent's method virtual and the child's version override so that a call through the parent type runs the child's code.
    • Code written against the parent type works unchanged for every child class, including ones added later.

    Key takeaways

    • Each class holds only what is different about it. Here that is two short formulas per class.
    • The parent's own version still matters: it is the standard option, and any child that does not override a method inherits it unchanged.
    • Overriding replaces behaviour, it does not add to it. Use base.Cost(weight) when the child wants the parent's answer as a starting point.
    • When a result looks wrong for one option, check that child's formula first: the other classes cannot affect it.
    Keep going with C#500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free