DEV Community

Kiron Roy
Kiron Roy

Posted on • Updated on

C# Interfaces & Inheritance in action

This article ties inheritance and interfaces.

A working sample is here: Ghostbusters Store

It is a lot of code for what this small application does. This is more about working with inheritance, interfaces and using these features inside of lists.

Interfaces are like agreements or "contracts" on what a class can do. Classes can have multiple interfaces, but classes cannot inherit multiple classes. Classes inheriting from more than one class is known as multiple-inheritance. C# does not allow multiple-inheritance.

Full code below

Interfaces

Interface (3) Implementation
IInventoryItem Base Interface
IPurchasable IInventoryItem
IRentaltable IInventoryItem
IInventory.cs
  • Base interface with 2 properties that all interfaces will have:
    1. A product name.
    2. How much of the product is in stock.

namespace ConsoleUI.Interfaces
{
    public interface IInventoryItem
    {
        string ProductName { get; set; }
        int QuantityInStock { get; set; }
    }
}
Enter fullscreen mode Exit fullscreen mode
IRentaltable.cs

IRentables will have two methods

  1. An option to rent a item.
  2. An option to return that item.
namespace InheritanceAndInterface
{
    public interface IRentaltable : IInventoryItem
    {
        void Rent();
        void ReturnRental();
    }

}
Enter fullscreen mode Exit fullscreen mode
IPurchasable.cs

IPurchasable will have one method: to purchase an item.

namespace InheritanceAndInterface
{
    public interface IPurchasable : IInventoryItem
    {
        void Purchase();
    }

}
Enter fullscreen mode Exit fullscreen mode

Classes

Classes (4) Implementation
InventoryItem Base Class
GuideModel InventoryItemModel, IPurchasable
ProtonPackModel InventoryItemModel, IRentaltable
VehicleModel InventoryItemModel, IPurchasable, IRentaltable
InventoryItemModel.cs

The class InventoryItem implements (use the contract) IInventoryItem

  1. has to have a product name.
  2. and how much quantity in stock.

using ConsoleUI.Interfaces;

namespace ConsoleUI.Models
{
    public class InventoryItemModel : IInventoryItem
    {
        public string ProductName { get; set; }
        public int QuantityInStock { get; set; }
    }

}
Enter fullscreen mode Exit fullscreen mode
VehicleModel.cs

A vehicle can be rented or purchased so it uses both IPurchasable and IRentaltable interfaces.

using ConsoleUI.Interfaces;
using System;

namespace ConsoleUI.Models
{
    public class VehicleModel : InventoryItemModel, IPurchasable, IRentaltable
    {
        public decimal DealerFee { get; set; }

        public void Purchase()
        {
            QuantityInStock -= 1;
            Console.WriteLine("This vechicle has been purchased");
        }

        public void Rent()
        {
            QuantityInStock -= 1;
            Console.WriteLine("This vehicle has been rented");
        }

        public void ReturnRental()
        {
            QuantityInStock += 1;
            Console.WriteLine("This vechicle has been returned");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
ProtonPackModel.cs

A Proton pack can only be rented so it only uses the IRentaltable Interface.


using ConsoleUI.Interfaces;
using System;

namespace ConsoleUI.Models
{
    public class ProtonPackModel : InventoryItemModel, IRentaltable
    {
        public void Zap()
        {
            Console.WriteLine("Zapping ghosts and feeling like a true winner");
        }

        public void Rent()
        {
            QuantityInStock -= 1;
            Console.WriteLine("This proton pack has been rented");
        }

        public void ReturnRental()
        {
            QuantityInStock += 1;
            Console.WriteLine("The proton pack has been returned");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
GuideModel.cs

Guide Books can only be purchased so it only implements IPurchasable interface.

using ConsoleUI.Interfaces;
using System;

namespace ConsoleUI.Models
{
    public class GuideModel : InventoryItemModel, IPurchasable
    {
        public int NumberOfPages { get; set; }

        public void Purchase()
        {
            QuantityInStock = -1;
            Console.WriteLine("This book has been purchased.");
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Main entry point in the C# console application

Program.cs
using ConsoleUI.Interfaces;
using ConsoleUI.Models;
using System;
using System.Collections.Generic;


namespace ConsoleUI
{
    class Program
    {
        static void Main(string[] args)
        {
            List<IRentaltable> rentables;
            List<IPurchasable> purchasables;

            ListofPurchasablesAndRentables(out rentables, out purchasables);

            clientsRentOrBuyOptionMenu(rentables, purchasables);

            EndOfAppPrompmt();
        }


        private static void clientsRentOrBuyOptionMenu(List<IRentaltable> rentables, List<IPurchasable> purchasables)
        {
            Console.WriteLine(" \n *** Welcome to Ghostbusters Supply Store ***\n");
            Console.WriteLine(" Do you want to rent or purchase something: (rent, purchase)\n");
            string rentalDecision = Console.ReadLine();

            if (rentalDecision.ToLower() == "rent")
            {
                foreach (IRentaltable item in rentables)
                {
                    Console.WriteLine($"Item: { item.ProductName }");
                    Console.Write("Do you want to rent this item (yes/no):");
                    string wantToRent = Console.ReadLine();

                    if (wantToRent.ToLower() == "yes")
                    {
                        item.Rent();
                    }

                    Console.Write("Do you want to return this item (yes/no):");
                    string wantToReturn = Console.ReadLine();

                    if (wantToReturn.ToLower() == "yes")
                    {
                        item.ReturnRental();
                    }
                }
            }
            else
            {
                foreach (var item in purchasables)
                {
                    Console.WriteLine($"Item: {item.ProductName}");
                    Console.Write("Do you want to purchase this product (yes/no): ");
                    string wantToPurchase = Console.ReadLine();

                    if (wantToPurchase.ToLower() == "yes")
                    {
                        item.Purchase();
                    }
                }
            }
        }

        private static void ListofPurchasablesAndRentables(out List<IRentaltable> rentables, out List<IPurchasable> purchasables)
        {
            rentables = new List<IRentaltable>();
            purchasables = new List<IPurchasable>();

            // Vechicles declared 
            var teslaVehicle = 
new VehicleModel 
{ DealerFee = 100, ProductName = "Tesla G series" };
            var ectoOneVehicle = new VehicleModel { DealerFee = 300, ProductName = "Ecto-1" };

            // Guides declared
            var ghostGuidebook = new GuideModel { ProductName = "How to Hunt Ghosts: A Practical Guide", NumberOfPages = 286 };
            var ghostHuntingbook = new GuideModel { ProductName = "Ghost Hunting for Beginners: Everything You Need to Know to Get Started", NumberOfPages = 324 };

            // Proton packs declared
            var acmePack = new ProtonPackModel 
{ ProductName = "Acme Twin-Copper", QuantityInStock = 3 };
            var hitachiZpack = new ProtonPackModel 
{ ProductName = "Hitachi Series Z", QuantityInStock = 1 };

            // Add rentables to the Rentable list
            rentables.Add(teslaVehicle); 
            rentables.Add(ectoOneVehicle);
            rentables.Add(acmePack); 
            rentables.Add(hitachiZpack);

            // Add purchasables to the Purchasable list
            // a book is only purchasable
            purchasables.Add(ghostGuidebook); 
            purchasables.Add(ghostHuntingbook);

            // A vehicle can be both rented or purchased
            // a vehicle is from the Class VehicleModel 
            // it is a reference not a copy, no memory issues!!!
            purchasables.Add(teslaVehicle);
            purchasables.Add(ectoOneVehicle);
        }

        private static void EndOfAppPrompmt()
        {
            Console.WriteLine(); // breakline
            Console.WriteLine("We are done here :)");

            Console.ReadLine();
        }

    }

}
Enter fullscreen mode Exit fullscreen mode

Discussion (4)

Collapse
jessesingleton profile image
Jesse Singleton

Hey! Nice article, although a little more explanation than code would have helped for people who are learning C#; as I assume that's who you are writing this article for.

Also your working example doesn't compile. There are typos on line 17 and line 23 of ProtonPackModel. You need to replace these with Console.WriteLine to compile successfully!

Collapse
kironroy profile image
Kiron Roy Author

Fixed! Not sure what happened to the Repl. I think is article may not be for beginners, so I'll take that tag out and clean the code.

Kind Regards,
Kiron Roy

Collapse
pomfrit123 profile image
***

Whats the purpose of the base class InventoryItemModel, cant you just use the interface? Im learning so sorry if it is a stupid question :)

Collapse
kironroy profile image
Kiron Roy Author • Edited on

The long answer is here: Interface vs Base class, look at answer with 484 votes. Some of these answers are opinionated, so with experience you will come up with the right implementation.

I am also learning, but heard that this is where Object Oriented Programming gets tricky.

I made another lighter version of this article: C# Interfaces & Inheritance v2 and that makes it clear for me with a simple console program.