© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
G. ByrneTarget C#https://doi.org/10.1007/978-1-4842-8619-7_14

14. Interfaces

Gerard Byrne1  
(1)
Belfast, Ireland
 

Interfaces and Abstract Classes

We learned in Chapter 13 that classes contain methods and fields . We also read in Chapter 12 on methods that

The methods we have not created but have used, they all have one thing in common.

They all live inside a class; they are part of a class.

We also said that

The crucial takeaway from the last chapter and a vital thing to remember in this chapter is that a class contains methods and variables.

So, in the last chapter, we saw that methods form a large part of the classes we created or that exist in the C# language. This is great as it helped us modularize our code. But let us think more on a larger scale than one or two classes. Let us use two examples, so we can think about different developers creating separate classes and methods around the same “topic” or “idea.”

Example 1

A number of developers are writing an ecommerce application that will work for different countries, and one of the methods required is to calculate the value-added tax for a product. Looking at the role of the developers, we see that
  • Developer 1 is writing for country 1, so they develop their class called Billing and they create a method called VatCalculation() , where the business logic is to multiply the item price by 20% (0.2) and return the amount.

  • Developer 2 is writing for country 2, so they develop their class called Billing and they create a method called TaxCalculation() , where the business logic is to multiply the item price by 15% (0.15) and return the amount.

  • Developer 3 is writing for country 3, so they develop their class called Billing and they create a method called CalculateTax() , where the business logic is to multiply the item price by 10% (0.10) and return the amount.

Now we should ask, "Why do we have three Billing classes all doing the same thing, which have a common method that calculates the VAT amount, but the method names are not the same?" Well, the answer is because we can! But is this a good example of standardization or clean code? Maybe not.

Now think about the same problem but where the three developers collaborate and discuss what they are about to do. They might suggest the methods they will use and the naming conventions. Sound like a plan? Well, this is where an abstract class or an interface could help. By using an abstract class or an interface, we can declare the method signatures and return types to be used, but not the code to be used, as the code will be decided by the individual developers and be appropriate to their situation.

In C#, a method signature is used by the C# compiler to ensure we have unique methods, that is, no two methods can have the same method signature. And we saw this when we looked at method overloading. We can now apply the concept of method signatures to an abstract class or interface because, within it we only declare the method signatures we require, not the actual body for the method. The actual body for the method will be supplied by the developer when the method is coded in a class that makes use of the abstract class or interface. Table 14-1 shows some method signatures.
Table 14-1

Method signature examples

Method signature

Description

CalculateTax(double itemPrice)

Method name is CalculateTax.

Parameter is of type double.

CalculateTax(double itemPrice, int quantity)

Method name is CalculateTax.

First parameter is of type double.

Second parameter is of type int.

When we talk about a method signature, we are not including the return type of the method. We cannot have the same method signature with different return types.

When we discussed overloaded methods, we said they were methods with the same name but different parameters. There was no mention of the return type. Our two CalculateTax() methods, in Table 14-1, show an example of method overloading.

Example 2

A number of developers are writing an application for an online insurance company who insures computer hardware in a country with different regions. Each developer is assigned to a different region, and they must have methods that calculate the regional rate and the hardware rate, before calculating the quote. Looking at the role of the developers, we see that
  • Developer 1 is writing for region A, so they develop their class called Quote and they create a method called RegionalRateCalculation() , where the business logic is to look up a struct and return the rate as a double for this region, for example, 0.05.

  • Developer 2 is writing for region B, so they develop their class called Quote and they create a method called RegionBRate() , where the business logic is to look up the same struct and return the rate as a double for this region, for example, 0.10.

  • Developer 3 is writing for region C, so they develop their class called Quote and they create a method called CRate() , where the business logic is to look up the same struct and return the rate as a double for this region, for example, 0.20.

There are therefore three classes called Quote all doing the same thing and having a common method that looks up the regional rate amount, but the method names are not the same. Surely, we can do better than this!

Well, this is where an abstract class or an interface could help.

The Interface or Abstract Class as a Manager

Example 3

Think about the abstract class or interface as being like a manager. In this scenario a manager gives three of their employees – Gerry, May, and June – the same request: order ten pizzas for the C# book launch at the “Build Your Core Programming Muscle” event tomorrow. So what might happen?
  • Gerry orders ten margherita pizzas of size 10 inches.

  • May orders five margherita and five pepperoni pizzas of size 16 inches.

  • June orders four pepperoni, two BBQ chicken, and four vegetable pizzas of size 16 inches.

Brilliant! All three employees have fulfilled the manager request, so they all have one thing in common: they have ten pizzas. However, the implementation of the request for ten pizzas was very different as can be seen from the different selection of pizzas each employee has chosen. This is perfectly acceptable as the manager request has been fulfilled.

When we write code or when we look back at the developers in Examples 1 and 2, we can see that a “manager” could be useful to set some upfront guidelines. We should think of the abstract class or interface as the manager giving some guidelines. Now, we might ask ourselves, “How can we apply the manager concept to the preceding Examples 1 and 2?” Well, Figure 14-1 shows how we could apply the abstract class and the classes to the ecommerce application example. We will use the idea of the abstract class for now, but the same principles apply to using an interface, with some differences.

A flowchart depicts the extension of the abstract class divided into country one class, country two class, and country three which has a concrete class and concrete method.

Figure 14-1

Hierarchy for the abstract and concrete classes

Now let us look at the example code that could be used to satisfy the structure shown in Figure 14-1. In looking at the application code, we will relate it to the manager scenario. The manager gives three of their staff the same instructions in following a task: "Use this abstract class when you write your class."

The abstract class is supplied as
public abstract class EcommerceBilling
{
  // abstract method
  public abstract double TaxCalculation(double itemPrice);
} // End of the abstract class

Now that the three developers have been given the abstract class, what might they do with it when they write their code?

Sealed Class

We can make a class sealed so that it cannot be inherited by another class but it can still be instantiated. When designing a class, we may wish to indicate that the class is specialized and should not therefore need to be extended. In our example we will create sealed classes for each of the three countries as they will not be extended.

Developer 1 writes the country 1 class, which inherits from the EcommerceBilling abstract class :
public sealed CountryOne : EcommerceBilling
{
  public override double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.2;
  } // End of TaxCalculation() method
} // End of CountryOne class
Developer 2 writes the country 2 class, which inherits from the EcommerceBilling abstract class:
public sealed CountryTwo : EcommerceBilling
{
  public override double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.15;
  } // End of TaxCalculation() method
} // End of CountryTwo class
Developer 3 writes the country 3 class, which inherits from the EcommerceBilling abstract class:
public sealed CountryThree : EcommerceBilling
{
  public override double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.10;
  } // End of TaxCalculation() method
} // End of CountryThree class

All three classes have been developed by inheriting the EcommerceBilling abstract class, and therefore they are contracted to use the method called TaxCalculation() , which has a parameter of type double. Here, all three developers have written their classes and implemented the method as contracted to do so, but they have different business logic appropriate for their country, that is, 20%, 15%, or 10%. This is like the “manager” giving the instructions and the employees implementing them but with different business logic.

Brilliant! We have now seen, in theory, that an abstract class can be developed to have method signatures with a return type stated. What we should also see from the example code is that we are using an abstract class, not a full class. It is not a full class because it is not complete; it has abstract methods, and these have no code. An abstract class can also have concrete methods, methods with code. We refer to the incomplete class as an abstract class, whereas a full class is referred to as a concrete class . The preceding classes, CountryOne, CountryTwo, and CountryThree, are all concrete classes as they are complete.

As we have seen, the information in the base class, the abstract class, has only general methods, methods with a return type and a signature. It is incumbent on each implementing class, the derived class, to add its own details. The abstract class therefore decides the nature of the methods that any derived classes must implement, but it will not provide an implementation of any of the defined methods.

Abstract classes can therefore be defined as incomplete classes. Abstract classes have the following characteristics :
  • The class is marked with the abstract keyword.

  • They contain one or more incomplete methods called abstract methods.

  • They provide the signature or declaration of the abstract methods; they leave the implementation of these methods to derived or subclasses.

  • They cannot be instantiated as they are incomplete.

  • They cannot be static.

  • They can contain a constructor.

  • Their methods are marked with the abstract keyword, as they are abstract methods.

  • They can have concrete methods, that is, methods with a body of code.

  • A class inheriting an abstract class must implement all the abstract methods in the abstract class, or it too must be declared as an abstract class.

  • A class inheriting an abstract class and implementing all its abstract methods is called the concrete class of the abstract class.

  • Methods in the inheriting class, the derived class, must include the keyword override before the method name when they are using the abstract methods.

  • They cannot be used for multiple inheritance. In other words, if we have an EcommerceBilling abstract class and an EcommercePayment abstract class, then a derived class such as CountryOne could not implement both of these abstract classes:

public class CountryOne : EcommerceBilling, EcommercePayment

This line or any format does not work. There is no multiple inheritance.

Let's code some C# and build our programming muscle.

Add a new project to hold the code for this chapter.
  1. 1.

    Right-click the solution CoreCSharp.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Project.

     
  4. 4.

    Choose Console App from the listed templates that appear.

     
  5. 5.

    Click the Next button.

     
  6. 6.

    Name the project Chapter14 and leave it in the same location.

     
  7. 7.

    Click the Next button.

     
  8. 8.

    Choose the framework to be used, which in our projects will be .NET 6.0 or higher.

     
  9. 9.

    Click the Create button.

     
Now we should see the Chapter14 project within the solution called CoreCSharp .
  1. 10.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 11.

    Click the Set as Startup Project option.

     

Notice how the Chapter14 project name has been made to have bold text, indicating that it is the new startup project and that it is the Program.cs file within it that will be executed when we run the debugging.

Amend the name of the Program.cs file.

Remember the coding principle of “self-documenting” code? Of course we do. So let us now rename the Program.cs file .
  1. 12.

    Right-click the Program.cs file in the Solution Explorer window.

     
  2. 13.

    Choose Rename.

     
  3. 14.

    Change the name to VATCalculator.cs.

     
  4. 15.

    Press the Enter key.

     
  5. 16.

    Amend the VATCalculator class, as in Listing 14-1, to have a namespace, class, and Main() method.

     
namespace Chapter14
{
  internal class VATCalculator
  {
    static void Main(string[] args)
    {
    } // End of Main() method
  } // End of VATCalculator class
} // End of Chapter14 namespace
Listing 14-1

Class template with a Main() method

  1. 17.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 18.

    Choose Add.

     
  3. 19.

    Choose Class.

     
  4. 20.

    Name the class as AbstractVATCalculations.cs.

     
  5. 21.

    The AbstractVATCalculations.cs class code will appear in the editor window and will be similar to Listing 14-2.

     
namespace Chapter14
{
  internal class AbstractVATCalculations
  {
  } // End of AbstractVATCalculations class
} // End of Chapter14 namespace
Listing 14-2

Class no methods, not yet an abstract class

  1. 22.

    Amend the code, as in Listing 14-3, to make it an abstract class using the keyword abstract.

     
namespace Chapter14
{
  internal abstract class AbstractVATCalculations
  {
  } // End of AbstractVATCalculations class
} // End of Chapter14 namespace
Listing 14-3

Abstract class no methods

  1. 23.

    Amend the code, as in Listing 14-4, to add two abstract methods to the abstract class.

     
  internal abstract class AbstractVATCalculations
  {
    /*
    Declare two incomplete methods which will be detailed in
    the inherited class. Each is an abstract method - a method
    signature and an access modifier
    */
    public abstract double CalculateVAT();
    public abstract double CalculateTotalPrice();
  } // End of AbstractVATCalculations class
} // End of Chapter14 namespace
Listing 14-4

Abstract methods in the abstract class

Code Analysis

The AbstractVATCalculations class contains an abstract method CalculateVAT(), which could be used later to calculate the amount of VAT to be added to an item. The “body” of the method will be decided by the actual class that inherits the abstract class.

The CalculateVAT() method has therefore been declared as abstract so that any subclasses will need to provide their own criteria for calculating the VAT. We also have a CalculateTotalPrice() abstract method declared, and this will also need to be made into a concrete method that returns a value of type double.

Instantiate the Abstract Class?

Let's look and see what happens if we try to instantiate this abstract class from within the Main() method. Will it be OK, or will we get an error? Well, we did read earlier that we cannot instantiate an abstract class, so let us code an example and prove that this is true.
  1. 24.

    Open the VATCalculator class and amend the code, as in Listing 14-5, to try and instantiate the AbstractVATCalculations abstract class.

     
internal class VATCalculator
{
 static void Main(string[] args)
 {
 Console.WriteLine("We cannot instantiate an abstract class");
 AbstractVATCalculations taxCalc = new AbstractVATCalculations();
 } // End of Main() method
} // End of VATCalculator class
Listing 14-5

Instantiate an abstract class

  1. 25.

    Hover over the red underline under the AbstractVATCalculations(), as shown in Figure 14-2, to reveal the error message that tells us we cannot create an instance of the abstract type or interface.

     

A screenshot depicts an error message. The text reads cannot instantiate an abstract class.

Figure 14-2

Error message – cannot create an instance of an abstract class

  1. 26.

    Remove the two lines of code we added in Listing 14-5, so that there is no code within the Main() method.

     
Create a class that inherits from the abstract class.
  1. 27.

    Right-click the Chapter14 project in the Solution Explorer panel .

     
  2. 28.

    Choose Add.

     
  3. 29.

    Choose Class.

     
  4. 30.

    Name the class as VATCalculations.cs.

     
  5. 31.

    The VATCalculations class code will appear and be similar to the code shown in Listing 14-6.

     
namespace Chapter14
{
  internal class VATCalculations
  {
  } // End of VATCalculations class
} // End of Chapter14 namespace
Listing 14-6

Concrete class template

  1. 32.

    Amend the VATCalculations class code , as in Listing 14-7, to add a member.

     
  internal class VATCalculations
  {
    private double itemPrice;
  } // End of VATCalculations class
} // End of Chapter14 namespace
Listing 14-7

Add a member, field

  1. 33.

    Amend the VATCalculations class code , as in Listing 14-8, to add a constructor that will set the value of the member.

     
  internal class VATCalculations
  {
    private double itemPrice;
    public VATCalculations(double itemPrice)
    {
      this.itemPrice = itemPrice;
    } // End of VATCalculations constructor
  } // End of VATCalculations class
} // End of Chapter14 namespace
Listing 14-8

Add a constructor that overrides the default constructor

  1. 34.

    Amend the VATCalculations code to inherit the AbstractVATCalculations class , as in Listing 14-9.

     
namespace Chapter14
{
  internal class VATCalculations : AbstractVATCalculations
  {
    private double itemPrice;
Listing 14-9

Make the class inherit the abstract class

There will be an error line under the VATCalculations class name because this class does not implement the method that was declared in the abstract class. We now need to implement the required method and complete the contract.
  1. 35.

    Amend the VATCalculations class code to implement the CalculateVAT() abstract method, as in Listing 14-10.

     
    public VATCalculations(double itemPrice)
    {
      this.itemPrice = itemPrice;
    } // End of VATCalculations constructor
    public override double CalculateVAT()
    {
      return this.itemPrice * 0.20;
    } // End of CalculateVAT() method
  } // End of VATCalculations class
} // End of Chapter14 namespace
Listing 14-10

Add the code to implement the method CalculateVAT()

  1. 36.

    Amend the VATCalculations class code to implement the CalculateTotalPrice() abstract method, as in Listing 14-11.

     
    public override double CalculateVAT()
    {
      return this.itemPrice * 0.20;
    } // End of CalculateVAT() method
    public override double CalculateTotalPrice()
    {
      return itemPrice + CalculateVAT();
    } // End of CalculateTotalPrice() method
  } // End of VATCalculations class
} // End of Chapter14 namespace
Listing 14-11

Add the code to implement the method CalculateTotalPrice()

Now we will instantiate the VATCalculations class, which has inherited from the AbstractVATCalculations abstract class, passing it the item price. We will then call the CalculateVAT() method and assign the returned VAT value to a variable, before displaying the VAT and the total price.
  1. 37.

    Open the VATCalculator class and amend the code, as in Listing 14-12.

     
static void Main(string[] args)
    {
      VATCalculations myCalculations = new VATCalculations(100);
      double vatAmount = myCalculations.CalculateVAT();
      Console.WriteLine($"{"VAT due on the item is",-30} £{vatAmount}");
      Console.WriteLine($"{"The total item cost is",-30} £{myCalculations.CalculateTotalPrice()} ");
    } // End of Main() method
Listing 14-12

Instantiate the VATCalculations class

  1. 38.

    Click the File menu.

     
  2. 39.

    Choose Save All.

     
  3. 40.

    Click the Debug menu.

     
  4. 41.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 14-3, displaying the total cost as 100 plus the 20% of 100, which is 20, giving the total of 120.

A screenshot of the console window depicts the implementation of the two methods for the item price as 100 and Vat on 100 equals 20.

Figure 14-3

Two methods have been implemented

  1. 42.

    Press the Enter key to close the console window.

     
Once we have an abstract class, we can develop as many concrete classes as we wish, with each class inheriting from the base abstract class. The only stipulation is that each concrete class must provide a definition for each abstract method. Let’s code an example to demonstrate that when we create a new class that inherits the abstract class, we change the code in the class but the code in the Main() will be the same, except we instantiate the new class.
  1. 43.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 44.

    Choose Add.

     
  3. 45.

    Choose Class.

     
  4. 46.

    Name the class as VATCalculationsFifteenPercent.cs.

     
  5. 47.

    Amend the code as in Listing 14-13.

     
namespace Chapter14
{
  internal class VATCalculationsFifteenPercent : AbstractVATCalculations
  {
    private double itemPrice;
    public VATCalculationsFifteenPercent(double itemPrice)
    {
      this.itemPrice = itemPrice;
    } // End of VATCalculations constructor
    public override double CalculateVAT()
    {
      return this.itemPrice * 0.15;
    } // End of CalculateVAT() method
    public override double CalculateTotalPrice()
    {
      return itemPrice + CalculateVAT();
    } // End of CalculateTotalPrice() method
  } // End of VATCalculationsFifteenPercent class
} // End of Chapter14 namespace
Listing 14-13

Add the code and use 15%, which is 0.15

  1. 48.

    Open the VATCalculator class that has the Main() method and add the code to instantiate the newly created VATCalculationsFifteenPercent and comment the first instantiation, as in Listing 14-14.

     
  static void Main(string[] args)
  {
    //VATCalculations myCalculations = new VATCalculations(100);
    VATCalculationsFifteenPercent myCalculations = new VATCalculationsFifteenPercent(100);
Listing 14-14

Change the class being instantiated

  1. 49.

    Click the File menu.

     
  2. 50.

    Choose Save All.

     
  3. 51.

    Click the Debug menu.

     
  4. 52.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 14-4, displaying the total cost as 100 plus the 15% of 100, which is 15, giving the total of 115.

A screenshot of the console window depicts the implementation of the two methods for the item price as 100 and Vat on 100 at 15 percent.

Figure 14-4

Two methods have been implemented

  1. 53.

    Press the Enter key to close the console window.

     

Static Members of the Abstract Class

When we have a static member in the abstract class, we can call it directly without having to use the instance of the class. Let's really think hard about this one sentence, because it is very important that we understand the meaning of static. In all the examples we have coded up to now, we have used static. Two examples are shown in Listings 14-15 and 14-16.
    public static void Main(string[] args)
    {
    } // End of Main() method
Listing 14-15

The static keyword in the Main() method

internal class MethodsValue
{
    static String[] repairShopClaims = new String[8];
    static string repairShopID;
    static string vehiclePolicyNumber;
    static double claimAmount;
    static DateTime claimDate;
    static int numberOfClaimsBeingMade;
    static int numberOfClaimsEntered = 0;
    static int arrayPositionCounter = 0;
    static double totalOfAllClaims;
    static double vatAmount;
    static void Main(string[] args)
  {
Listing 14-16

The static keyword with variables in program MethodsValue

So static, when related to the field, means that the field is part of the class; it does not belong to the instance that we make of the class. Yes, we did read earlier that an abstract class cannot be instantiated, but for now let us just deal with the concept of static.

In trying to understand static, let us think again of a manager scenario. The manager is a very busy person, so they decide to make two instances of themselves and give the instances their characteristics, except their mobile phone number. Looking at this we could represent it as shown in Table 14-2.
Table 14-2

The manager characteristic and the instance characteristics

Manager

myManagerInstance1

myManagerInstance2

Approve leave

Approve leave

Approve leave

Attend meeting

Attend meeting

Attend meeting

Answer calls

  
The manager has reserved the answer calls characteristic. Only they can answer calls; the two instances can approve leaves and attend meetings. In terms of classes, we would say that
  • Approve leave is an instance variable; it belongs to, or is only associated with, the instance of the class, not the actual class.

  • Attend meeting is an instance variable; it belongs to, or is only associated with, the instance of the class, not the actual class.

  • Answer calls is a static variable; it belongs to the class and not to any of the two instances of the class.

This is very useful for the manager, the manager class, as it can now keep a record of the number of calls directly. Now, as an employee who wants to have leave approved, we might go to manager instance 1 or manager instance 2. And in terms of writing C# code, we would say something like this:

myManagerInstance1.approveLeave

or

myManagerInstance1.approveLeave

But if we wanted to phone the manager, we would have to do this directly, and in terms of writing C# code, we would say something like this:

Manager.answerCall

Now, going back to abstract classes and our static variable, we are saying that it belongs to the abstract class. Remember, we cannot make an instance of an abstract class. Also, when we move on to look at interfaces shortly, we will see that we cannot instantiate an interface. Static equals fixed, does not move; it is stuck to the class or interface.
  1. 54.

    Amend the AbstractVATCalculations class code, as in Listing 14-17, to add a discount rate.

     
internal abstract class AbstractVATCalculations
 {
   public double discountRate = 0.10;
   /*
   Declare two incomplete methods which will be detailed in
   the inherited class. Each is an abstract method - a method
   signature and an access modifier
   */
   public abstract double CalculateVAT();
Listing 14-17

Add the variable discountRate

Now we will add a method in the AbstractVATCalculations class that contains the formula to calculate the discount amount, based on the field discountRate. We could add this method to the VATCalculations class, but if we think about this method, we will see that it is logic that can be shared, so we will put it into the AbstractVATCalculations class . This means we will be calling the abstract class method directly; there is no instance of an abstract class.
  1. 55.

    In the AbstractVATCalculations class, add the method, as in Listing 14-18.

     
    public abstract double CalculateVAT();
    public abstract double CalculateTotalPrice();
    public double CalculateDiscountedAmount()
    {
      return CalculateTotalPrice() * discountRate;
    } // End of CalculateDiscountedAmount() method
  } // End of AbstractVATCalculations class
} // End of Chapter14 namespace
Listing 14-18

Add the code for the method CalculateDiscountedAmount()

  1. 56.

    In the VATCalculator class , amend the code, as in Listing 14-19, to call the method from within a WriteLine() method and set the instantiated class back to the original VATCalculations rather than VATCalculationsFifteenPercent.

     
namespace Chapter14
{
  internal class VATCalculator
  {
    static void Main(string[] args)
    {
      VATCalculations myCalculations = new VATCalculations(100);
      //VATCalculationsFifteenPercent myCalculations = new VATCalculationsFifteenPercent(100);
      double vatAmount = myCalculations.CalculateVAT();
      Console.WriteLine($"{"VAT due on the item is",-30} £{vatAmount}");
      Console.WriteLine($"{"The total item cost is",-30} £{myCalculations.CalculateTotalPrice()} ");
      Console.WriteLine($"{"The discounted amount is",-30} £{myCalculations.CalculateDiscountedAmount()}n");
    } // End of Main() method
  } // End of VATCalculator class
} // End of Chapter14 namespace
Listing 14-19

Add the code to call the method in the abstract class

  1. 57.

    Click the File menu.

     
  2. 58.

    Choose Save All.

     
  3. 59.

    Click the Debug menu.

     
  4. 60.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 14-5, displaying
  • The total cost as 100 plus the 20% of 100, which is 20, giving the total item cost of 120.

  • A discount of 10%, as per the static variable, of the 120, which is 12.

A screenshot of the console window depicts the approach of the static variable for the item price as 100 and Vat on 100 equals 20.

Figure 14-5

The static variable has been accessed and worked correctly.

  1. 61.

    Press the Enter key to close the console window.

     

Let's code another abstract class using the countries example.

We read in the earlier example the following:

A number of developers are writing an application for an online insurance company who insure computer hardware in a country with different regions. Each developer is assigned to a different region, and they must have methods that calculate the regional rate and the hardware rate, before calculating the quote.

Add a new folder to the Chapter 14 project to hold the code for this example.
  1. 1.

    Right-click the Chapter14 project name.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Folder.

     
  4. 4.

    Name the folder Example2.

     
  5. 5.

    Right-click the Example2 folder.

     
  6. 6.

    Choose Add.

     
  7. 7.

    Choose Class.

     
  8. 8.

    Click the Add button.

     
  9. 9.

    Name the class EcommerceBilling.cs.

     
  10. 10.

    Click the Create button.

     
The EcommerceBilling.cs class code will appear in the editor window and will be similar to Listing 14-20.
namespace Chapter14.Example2
{
  // abstract class
  internal class EcommerceBilling
  {
  } // End of EcommerceBilling class
} // End of Chapter14 namespace
Listing 14-20

New class template code

  1. 11.

    Amend the code, as in Listing 14-21, to make the class abstract and add an abstract method.

     
namespace Chapter14.Example2
{
  // abstract class
  internal abstract class EcommerceBilling
  {
   // abstract method
    public abstract double TaxCalculation(double itemPrice);
  } // End of EcommerceBilling class
} // End of Chapter14 namespace
Listing 14-21

Make class abstract and add an abstract method

This will mean that any class that uses this abstract class as its base class will have to implement the TaxCalculation() method by adding code to it, making it a concrete method in a concrete class.

Make the concrete classes, which have to inherit from the abstract class.
  1. 12.

    Right-click the Example2 folder in the Solution Explorer panel.

     
  2. 13.

    Choose Add.

     
  3. 14.

    Choose Class.

     
  4. 15.

    Name the class as CountryOne.cs.

     
  5. 16.

    Amend the CountryOne class code to have it inherit the EcommerceBilling class, and then implement the method with some code specific to this country’s tax rate for the item, as in Listing 14-22.

     
namespace Chapter14.Example2
{
  internal class CountryOne : EcommerceBilling
  {
    public override double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.2;
    } // End of TaxCalculation() method
  } // End of CountryOne class
} // End of Chapter14.Example2 namespace
Listing 14-22

Country 1 class inherits the abstract class and implements the method

  1. 17.

    Right-click the Example2 folder in the Solution Explorer panel.

     
  2. 18.

    Choose Add.

     
  3. 19.

    Choose Class.

     
  4. 20.

    Name the class as CountryTwo.cs.

     
  5. 21.

    Amend the CountryTwo class code to have it inherit the EcommerceBilling class, and then implement the method with some code specific to this country’s tax rate for the item, as in Listing 14-23.

     
namespace Chapter14.Example2
{
  internal class CountryTwo : EcommerceBilling
  {
    public override double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.15;
    } // End of TaxCalculation() method
  } // End of CountryTwo class
} // End of Chapter14.Example2 namespace
Listing 14-23

Country 2 class inherits the abstract class and implements the method

  1. 22.

    Right-click the project Example2 folder in the Solution Explorer panel.

     
  2. 23.

    Choose Add.

     
  3. 24.

    Choose Class.

     
  4. 25.

    Name the class as CountryThree.cs.

     
  5. 26.

    Amend the CountryThree class code to have it inherit the EcommerceBilling class, and then implement the method with some code specific to this country’s tax rate for the item, as in Listing 14-24.

     
namespace Chapter14.Example2
{
  internal class CountryThree : EcommerceBilling
  {
    public override double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.10;
    } // End of TaxCalculation() method
  } // End of CountryThree class
} // End of Chapter14.Example2 namespace
Listing 14-24

Country 3 class inherits the abstract class and implements the method

Make the concrete class that will contain the Main method and use the three country classes, which have inherited from the abstract class.
  1. 27.

    Right-click the project Example2 folder in the Solution Explorer panel.

     
  2. 28.

    Choose Add.

     
  3. 29.

    Choose Class.

     
  4. 30.

    Name the class as EcommerceApplication.cs.

     
  5. 31.

    Amend the EcommerceApplication class code to have it contain a Main() method and one member of type double that will hold a price for an item, as in Listing 14-25.

     
namespace Chapter14.Example2
{
  internal class EcommerceApplication
  {
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
    } // End of Main() method
  } // End of EcommerceApplication class
} // End of Chapter14.Example2 namespace
Listing 14-25

Class with a Main() method and one member

  1. 32.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 33.

    Choose Properties from the pop-up menu.

     
  3. 34.

    Choose the Chapter14.Example2.EcommerceApplication class in the Startup object drop-down list, as shown in Figure 14-6.

     

A screenshot depicts a startup object drop-down menu to change the class.

Figure 14-6

Changing the startup class in the C# project

  1. 35.

    Amend the EcommerceApplication class code to have it instantiate the CountryOne class, as in Listing 14-26.

     
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      CountryOne myCountryOne = new CountryOne();
    } // End of Main() method
Listing 14-26

Instantiate the CountryOne class using the default constructor

  1. 36.

    Amend the EcommerceApplication class code to have it display a message that includes a call to the method in the CountryOne class , as in Listing 14-27.

     
static void Main(string[] args)
{
  double itemPrice = 100.00;
  CountryOne myCountryOne = new CountryOne();
  Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryOne.TaxCalculation(itemPrice)}");
 } // End of Main() method
Listing 14-27

Display details of the item for CountryOne class

  1. 37.

    Click the File menu.

     
  2. 38.

    Choose Save All.

     
  3. 39.

    Click the Debug menu.

     
  4. 40.

    Choose Start Without Debugging.

     
Figure 14-7 shows the console window with the item price and the tax amount for country 1.

A screenshot of a console window depicts the Country One output method. The text reads the tax on an item of price 100 euros is 20 euros whereas country one tax rate is 20% and 20% of 100 = 20.

Figure 14-7

CountryOne output from the method

A screenshot of the console window depicts the implementation of three methods. Country 1 tax rate is 20% = 20, country 2 is 15% = 15, and country 3 is 10% = 10.

Figure 14-8

All three methods, one from each class, have run.

  1. 41.

    Press the Enter key to close the console window.

     
  2. 42.

    Amend the EcommerceApplication class code to have it instantiate the CountryTwo and CountryThree classes, as in Listing 14-28.

     
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      CountryOne myCountryOne = new CountryOne();
      CountryTwo myCountryTwo = new CountryTwo();
      CountryThree myCountryThree = new CountryThree();
Listing 14-28

Instantiate the CountryTwo and CountryThree classes

  1. 43.

    Amend the EcommerceApplication class code to have it display messages that include calls to the methods in the CountryTwo and CountryThree classes, as in Listing 14-29.

     
 CountryOne myCountryOne = new CountryOne();
 CountryTwo myCountryTwo = new CountryTwo();
 CountryThree myCountryThree = new CountryThree();
 Console.WriteLine($"The tax on an item of price " +
 $"£{itemPrice} is £{ myCountryOne.TaxCalculation(itemPrice)}");
   Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryOne.TaxCalculation(itemPrice)}");
   Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryTwo.TaxCalculation(itemPrice)}");
   Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryThree.TaxCalculation(itemPrice)}");
    } // End of Main() method
Listing 14-29

Display details of the item for CountryTwo and CountryThree

  1. 44.

    Click the File menu.

     
  2. 45.

    Choose Save All.

     
  3. 46.

    Click the Debug menu.

     
  4. 47.

    Choose Start Without Debugging.

     

The console window will appear, as Figure 14-8, and show the item price and the tax amount for each of the three countries.

  1. 48.

    Press the Enter key to close the console window.

     

Concept of an Interface

We have just used abstract classes, and we will now see that an interface is similar to an abstract class and is also used with abstraction and inheritance. There are some differences, however, and interfaces have the following characteristics :
  • They are marked with the interface keyword.

  • They contain one or more incomplete methods called interface methods, but we do not use the keyword abstract as the compiler would complain.

  • They provide the signature or declaration of the abstract methods; they leave the implementation of these methods to the class that implements the interface.

  • They cannot be instantiated as they are incomplete and they are interfaces.

  • From C# 8 an interface can have static members, for example, vatRate = 0.25. This means that the vatRate member can be called directly from the interface without having an instantiation.

Now the interesting one. If we have used or read about C# interfaces compared with abstract classes up to C# 7, then we may have read the following statement :

They cannot have concrete methods, that is, methods with a body.

Yes, that message could probably be embedded in our inner mind, but, like all things, they change. Indeed, when C# 8 was introduced, things about interfaces did change, and not to the liking of all in the developer community. With C# 8 we can now add concrete methods, methods with code. They are called default methods . So does this make it more like an abstract class? Well, the answer is possibly. However, for now, we will concentrate on pre–C# 8, so we get a strong understanding of interfaces as a concrete, and then we will add new features. Looking at an interface, some additional points are important before we start coding :
  • A class implementing the interface must implement all the interface methods in the interface class.

  • They can be used for multiple inheritance. In other words, if we have an EcommerceBilling interface class and an EcommercePayment interface class, then a derived class such as CountryOne could implement both of these interfaces:

public class CountryOne : IEcommerceBilling, IEcommercePayment

This line works as we can implement more than one interface. Looking back at the example we had earlier in the abstract class, we can now show how we could do the same thing using an interface.

Example

The manager gives three of their staff the same instructions in following a task: "Use this interface when you write your class."

The interface is supplied as in Listing 14-30.
// Interface
interface IEcommerceBilling
{
  // Abstract method in interface but no keyword abstract
  double TaxCalculation(double itemPrice);
}
Listing 14-30

Sample interface from manager

Now that the three developers have been given the interface, what might they do with it when they write their code?

Developer 1 writes the CountryOne class, which inherits from the IEcommerceBilling interface as in Listing 14-31.
class CountryOne : IEcommerceBilling
{
  public double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.2;
  } // End of TaxCalculation() method
} // End of CountryOne class
Listing 14-31

Developer 1's implementation of the interface

Developer 2 writes the CountryTwo class, which inherits from the IEcommerceBilling interface as in Listing 14-32.
class CountryTwo : IEcommerceBilling
{
  public double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.15;
  } // End of TaxCalculation() method
} // End of CountryTwo class
Listing 14-32

Developer 2's implementation of the interface

Developer 3 writes the CountryThree class, which inherits from the IEcommerceBilling interface as in Listing 14-33.
class CountryThree : IEcommerceBilling
{
  public double TaxCalculation(double itemPrice)
  {
    return itemPrice * 0.10;
  } // End of taxCalculation() method
} // End of CountryThree class
Listing 14-33

Developer 3's implementation of the interface

All three classes have been developed by implementing the IEcommerceBilling interface , and therefore they are contracted to use the method called TaxCalculation(), which has a parameter of type double. Here, all three developers have written their classes and implemented the method as contracted to do so, but they have different business logic appropriate for their country, that is, 20%, 15%, or 10% is used depending on the developer and their country. This is like the “manager” giving the instructions and the employees implementing them, but with different business logic.

Brilliant! We have now seen that an interface or an abstract class can be developed to have method signatures with a return type stated. What we should also see from the example code is that we are using an interface. The preceding classes, CountryOne, CountryTwo, and CountryThree, are all concrete classes.

Add a new folder to the Chapter 14 project to hold the code for this example.
  1. 1.

    Right-click the Chapter14 project name.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Folder.

     
  4. 4.

    Name the folder Example3Interfaces.

     
  5. 5.

    Right-click the Example3Interfaces folder.

     
  6. 6.

    Choose Add.

     
  7. 7.

    Choose New Item.

     
  8. 8.

    Choose Interface.

     
  9. 9.

    Name the class IEcommerceBilling.cs.

     
  10. 10.

    Click the Add button.

     
We are using the letter I to make it easy to see that this “class” is an interface. The use of an I as the initial letter is not essential, but the initial letter I will be used by many developers. Indeed, in the C# documentation, there are plenty of examples where the Microsoft developers have used the I to name an interface:
  • In the TextWriter class documentation, we are told that it represents a writer that can write a sequential series of characters, that it is abstract, and that it implements IDisposable and IAsyncDisposable.

  • In the String class documentation, we are told that the String represents text as a sequence of UTF-16 code units and that it implements IEnumerable<Char>, IEnumerable, IComparable, IComparable<String>, IConvertible, IEquatable<String>, and ICloneable.

Now continuing the example, we will see the IEcommerceBilling.cs class code will appear in the editor window and will be similar to Listing 14-34. Note the word interface.
namespace Chapter14.Example3Interfaces
{
  internal interface IEcommerceBilling
  {
  } // End of IEcommerceBilling interface
} // End of Chapter14.Example3Interfaces namespace
Listing 14-34

New interface template code

  1. 11.

    Amend the code , as in Listing 14-35, to add an “abstract method,” which means a return type and a signature.

     
  internal interface IEcommerceBilling
  {
    // interface method
    double TaxCalculation(double itemPrice);
  } // End of IEcommerceBilling interface
Listing 14-35

New interface with an interface method

This will mean that any class using this interface will have to implement the TaxCalculation() method by adding code to it.

Make the concrete classes, which have to inherit from the interface.
  1. 12.

    Right-click the project Example3Interfaces folder in the Solution Explorer panel.

     
  2. 13.

    Choose Add.

     
  3. 14.

    Choose Class.

     
  4. 15.

    Name the class as CountryOne.cs.

     
  5. 16.

    Click the Add button.

     
  6. 17.

    Amend the CountryOne class code, as in Listing 14-36, to have it inherit the IEcommerceBilling class and then implement the method with some code specific to this country’s tax rate for the item.

     
namespace Chapter14.Example3Interfaces
{
  internal class CountryOne : IEcommerceBilling
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.2;
    } // End of taxCalculation() method
  } // End of CountryOne class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-36

Country 1 class inherits the interface and implements the method

  1. 18.

    Right-click the project Example3Interfaces folder in the Solution Explorer panel.

     
  2. 19.

    Choose Add.

     
  3. 20.

    Choose Class.

     
  4. 21.

    Name the class as CountryTwo.cs.

     
  5. 22.

    Amend the CountryTwo class code, as in Listing 14-37, to have it inherit the IEcommerceBilling class and then implement the method with some code specific to this country’s tax rate for the item.

     
namespace Chapter14.Example3Interfaces
{
  internal class CountryTwo :IEcommerceBilling
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.15;
    } // End of TaxCalculation() method
  } // End of CountryTwo class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-37

Country 2 class inherits the interface and implements the method

  1. 23.

    Right-click the Example3Interfaces folder in the Solution Explorer panel.

     
  2. 24.

    Choose Add.

     
  3. 25.

    Choose Class.

     
  4. 26.

    Name the class as CountryThree.cs.

     
  5. 27.

    Amend the CountryThree class code, as in Listing 14-38, to have it inherit the IEcommerceBilling class and then implement the method with some code specific to this country’s tax rate for the item.

     
namespace Chapter14.Example3Interfaces
{
  internal class CountryThree :IEcommerceBilling
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.10;
    } // End of TaxCalculation() method
  } // End of CountryThree class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-38

Country 3 class inherits the interface and implements the method

Now we will make the class that will contain the Main method and use the three country classes, which have inherited from the interface.
  1. 28.

    Right-click the Example3Interfaces folder in the Solution Explorer panel.

     
  2. 29.

    Choose Add.

     
  3. 30.

    Choose Class.

     
  4. 31.

    Name the class as EcommerceApplication.cs.

     
  5. 32.

    Amend the EcommerceApplication class code to have it contain a Main() method and one member of type double, which will hold a price for an item, as in Listing 14-39.

     
namespace Chapter14.Example3Interfaces
{
  internal class EcommerceApplication
  {
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
    } // End of Main() method
  } // End of EcommerceApplication class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-39

Class with a Main() method and one member

  1. 33.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 34.

    Choose Properties from the pop-up menu.

     
  3. 35.

    Choose the Chapter14.Example3Interfaces.EcommerceApplication class in the Startup object drop-down list, as shown in Figure 14-9.

     

A screenshot of the console window depicts a startup object dropdown menu to change the startup class.

Figure 14-9

Changing the startup class in the C# project

  1. 36.

    Amend the EcommerceApplication class code to have it instantiate the CountryOne class, as in Listing 14-40.

     
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      CountryOne myCountryOne = new CountryOne();
    } // End of Main() method
Listing 14-40

Instantiate the CountryOne class

  1. 37.

    Amend the EcommerceApplication class code to have it display a message that includes a call to the method in the CountryOne class , as in Listing 14-41.

     
static void Main(string[] args)
{
  double itemPrice = 100.00;
  CountryOne myCountryOne = new CountryOne();
  Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryOne.TaxCalculation(itemPrice)}");
    } // End of Main() method
Listing 14-41

Display details of the item for CountryOne class

  1. 38.

    Click the File menu.

     
  2. 39.

    Choose Save All.

     
  3. 40.

    Click the Debug menu.

     
  4. 41.

    Choose Start Without Debugging.

     
The console window will appear and show the item price and the tax amount for country 1, as shown in Figure 14-10.

A screenshot of the console window depicts the CountryOne output. The text reads the tax on an item of price 100 euros is 20, whereas country one tax rate is 20%.

Figure 14-10

CountryOne output from the method

A screenshot of the console window depicts the three methods. Country 1 tax rate is 20% = 20, country 2 is 15% = 15, and country 3 is 10% = 10.

Figure 14-11

All three methods, one from each class, have run

  1. 42.

    Press the Enter key to close the console window.

     
  2. 43.

    Amend the EcommerceApplication class code to have it instantiate the CountryTwo and CountryThree classes, as in Listing 14-42.

     
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      CountryOne myCountryOne = new CountryOne();
      CountryTwo myCountryTwo = new CountryTwo();
      CountryThree myCountryThree = new CountryThree();
Listing 14-42

Instantiate the CountryTwo and CountryThree classes

  1. 44.

    Amend the EcommerceApplication class code to have it display messages that include calls to the methods in the CountryTwo and CountryThree classes, as in Listing 14-43.

     
 CountryOne myCountryOne = new CountryOne();
 CountryTwo myCountryTwo = new CountryTwo();
 CountryThree myCountryThree = new CountryThree();
  Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryOne.TaxCalculation(itemPrice)}");
      Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryTwo.TaxCalculation(itemPrice)}");
      Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryThree.TaxCalculation(itemPrice)}");
    } // End of Main() method
Listing 14-43

Display details of the item for CountryTwo and CountryThree

  1. 45.

    Click the File menu.

     
  2. 46.

    Choose Save All.

     
  3. 47.

    Click the Debug menu.

     
  4. 48.

    Choose Start Without Debugging.

     

The console window will appear, as Figure 14-11, and show the item price and the tax amount for each of the three countries.

  1. 49.

    Press the Enter key to close the console window.

     

Implementing Multiple Interfaces

When using interfaces, it is possible to implement multiple of them. In other words, if we have an IEcommerceBilling interface and an IEcommercePayment interface, then a class such as CountryOne could implement both of these interfaces. The syntax for this multiple inheritance would be
public class CountryOne : IEcommerceBilling, IEcommercePayment
So we will now code this and ensure that we can see the implementation of multiple interfaces by classes. The new IEcommercePayment interface will have three interface methods that will be used in the CountryOne class to calculate the extra charge on a transaction based on the card type and the transaction amount. There are only two card types for this example, credit card or debit card:
  • The first method will accept the card type and the transaction amount and pass the transaction amount to either the credit card payment method or the debit card payment method and return the additional fee value to the calling method.

  • The second method will be for the debit card payment fee, which will calculate 1%, 0.01, of the transaction amount and return the value to the calling method.

  • The third method will be for the credit card payment fee, which will calculate 2%, 0.02, of the transaction amount and return the value to the calling method.

Create a second interface.
  1. 1.

    Right-click the Example3Interfaces folder.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Item.

     
  4. 4.

    Choose Interface.

     
  5. 5.

    Name the interface IEcommercePayment.cs.

     
  6. 6.

    Click the Add button.

     
The IEcommercePayment.cs class code will appear in the editor window and will be similar to Listing 14-44. Note the word interface.
namespace Chapter14.Example3Interfaces
{
  internal interface IEcommercePayment
  {
  } // End of IEcommercePayment interface
} // End of Chapter14.Example3Interfaces namespace
Listing 14-44

Interface template code

  1. 7.

    Amend the code , as in Listing 14-45, to add the “abstract methods,” which means a return type and a signature.

     
namespace Chapter14.Example3Interfaces
{
  internal interface IEcommercePayment
  {
    // interface methods
    double PaymentMethod(String paymentType,
    double transactionAmount);
    double DebitCardPaymentFee(double debitAmount);
    double CreditCardPaymentFee(double creditAmount);
  } // End of IEcommercePayment interface
} // End of Chapter14.Example3Interfaces namespace
Listing 14-45

New interface with interface methods

This will mean that any class implementing this interface will have to implement all three of the interface methods by adding code to them.

Make the concrete class CountryOne inherit from this interface.
  1. 8.

    Open the CountryOne class , which is in the Example3Interfaces folder.

     
  2. 9.

    Amend the CountryOne class code to have it inherit this new interface IEcommercePayment, as well as the original IEcommerceBilling interface, and then implement the methods with some code specific to this country, as in Listing 14-46.

     
namespace Chapter14.Example3Interfaces
{
 internal class CountryOne : IEcommerceBilling, IEcommercePayment
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.2;
    } // End of taxCalculation() method
   public double PaymentMethod(String paymentType, double
       transactionAmount)
    {
      double cardFee;
      if (paymentType.Equals("Debit"))
      {
        cardFee = DebitCardPaymentFee(transactionAmount);
      } // End of if block
      else
      {
        cardFee = CreditCardPaymentFee(transactionAmount);
      }// End of else block
      return cardFee;
    } // End of paymentMethod() method
    public double DebitCardPaymentFee(double debitAmount)
    {
      return debitAmount * 0.01; // 1%
    } // End of debitCardPaymentFee() method
    public double CreditCardPaymentFee(double creditAmount)
    {
      return creditAmount * 0.02; // 2%
    } // End of creditCardPaymentFee() method
  } // End of CountryOne class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-46

Multiple inheritance and implementing all abstract methods

  1. 10.

    Open the EcommerceApplication class, which is in the Example3Interfaces folder.

     

In the EcommerceApplication class , we will now

  • Add a variable of type double called feeForUsingACard, which will hold the value passed back from the payment method in the CountryOne class.

  • Call the PaymentMethod() method in the CountryOne concrete class, passing to it a credit card and an amount of 200.

  • Display the returned fee amount from the method call. In this example for a credit card in this country, there is a 2% fee, so we should expect to get back 2% of 100, which is 2.

  1. 11.

    Amend the code, as in Listing 14-47.

     
static void Main(string[] args)
{
  double itemPrice = 100.00;
  double feeForUsingACard;
  CountryOne myCountryOne = new CountryOne();
  CountryTwo myCountryTwo = new CountryTwo();
  CountryThree myCountryThree = new CountryThree();
  Console.WriteLine($"The tax on an item of price " +
  $"£{itemPrice} is £{ myCountryOne.TaxCalculation(itemPrice)}");
  Console.WriteLine($"The tax on an item of price " +
  $"£{itemPrice} is £{ myCountryTwo.TaxCalculation(itemPrice)}");
 Console.WriteLine($"The tax on an item of price " +
  $"£{itemPrice} is £{ myCountryThree.TaxCalculation(itemPrice)}");
 feeForUsingACard = myCountryOne.PaymentMethod("Credit", itemPrice);
 Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
    } // End of Main() method
Listing 14-47

Add a variable, calling the payment method from the class

  1. 12.

    Click the File menu.

     
  2. 13.

    Choose Save All.

     
  3. 14.

    Click the Debug menu.

     
  4. 15.

    Choose Start Without Debugging.

     
The console window will appear as shown in Figure 14-12 and the card fee of 2.00 will be displayed.

A screenshot of a console window in which the calculated credit card transaction fee is displayed. The text reads, credit card transaction fee equals 2 percent of 100 equals 2.

Figure 14-12

Card fee for CountryOne calculated

  1. 16.

    Press the Enter key to close the console window.

     

This is great. We have coded a nice example that has allowed us to use implementation of more than one interface.

As an extension to this example, we will code for the CountryTwo and CountryThree classes, so they inherit the IEcommercePayment interface, as in Listings 14-46 and 14-47. This is really the same process we used when CountryOne inherited the interface. The main difference would be using different percentage rates in each class if that was required :

  • For CountryTwo , the rates might be as follows:
    • Debit card payment fee of 1.5%, 0.015, of the transaction amount

    • Credit card payment fee of 2.5%, 0.025, of the transaction amount

  • For CountryThree , the rates might be as follows:
    • Debit card payment fee of 2.0%, 0.02, of the transaction amount

    • Credit card payment fee of 3.0%, 0.03, of the transaction amount

  1. 17.

    Amend the code for the CountryTwo class, which is in the Example3Interfaces folder, as shown in Listing 14-48.

     
namespace Chapter14.Example3Interfaces
{
  internal class CountryTwo : IEcommerceBilling,IEcommercePayment
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.15;
    } // End of TaxCalculation() method
    public double PaymentMethod(String paymentType, double transactionAmount)
    {
      double cardFee;
      if (paymentType.Equals("Debit"))
      {
        cardFee = DebitCardPaymentFee(transactionAmount);
      } // End of if block
      else
      {
        cardFee = CreditCardPaymentFee(transactionAmount);
      }// End of else block
      return cardFee;
    } // End of PaymentMethod() method
    public double DebitCardPaymentFee(double debitAmount)
    {
      return debitAmount * 0.015; // 1.5%
    } // End of DebitCardPaymentFee() method
    public double CreditCardPaymentFee(double creditAmount)
    {
      return creditAmount * 0.025; // 2.5%
    } // End of CreditCardPaymentFee() method
  } // End of CountryTwo class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-48

CountryTwo inheriting the interfaces and implementing methods

  1. 18.

    Amend the code for the CountryThree class, which is in the Example3Interfaces folder, as shown in Listing 14-49.

     
namespace Chapter14.Example3Interfaces
{
  internal class CountryThree :IEcommerceBilling, IEcommercePayment
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.10;
    } // End of TaxCalculation() method
    public double PaymentMethod(String paymentType, double transactionAmount)
    {
      double cardFee;
      if (paymentType.Equals("Debit"))
      {
        cardFee = DebitCardPaymentFee(transactionAmount);
      } // End of if block
      else
      {
        cardFee = CreditCardPaymentFee(transactionAmount);
      }// End of else block
      return cardFee;
    } // End of PaymentMethod() method
    public double DebitCardPaymentFee(double debitAmount)
    {
      return debitAmount * 0.02; // 2%
    } // End of DebitCardPaymentFee() method
    public double CreditCardPaymentFee(double creditAmount)
    {
      return creditAmount * 0.03; // 3%
    } // End of CreditCardPaymentFee() method
  } // End of CountryThree class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-49

CountryThree inheriting the interfaces and implementing methods

  1. 19.

    Open the EcommerceApplication class, which is in the Example3Interfaces folder.

     
  2. 20.

    Amend the code, as in Listing 14-50, to call the PaymentMethod() methods from the CountryTwo and CountryThree concrete classes, passing to them a credit card and an amount of 200, and have it display the returned fee amounts from each call.

     

For a credit card for CountryTwo, there is a 2.5% fee, so we should expect to get back 2.5% of 100, which is 2.5.

For a credit card for CountryThree, there is a 3.0% fee, so we should expect to get back 3.0% of 100, which is 3.
feeForUsingACard = myCountryOne.PaymentMethod("Credit", itemPrice);
      Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
      feeForUsingACard = myCountryTwo.PaymentMethod("Credit", itemPrice);
      Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
      feeForUsingACard = myCountryThree.PaymentMethod("Credit", itemPrice);
      Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
    } // End of Main() method
  } // End of EcommerceApplication class
} // End of Chapter14.Example3Interfaces namespace
Listing 14-50

EcommerceApplication card fee calls

  1. 21.

    Click the File menu.

     
  2. 22.

    Choose Save All.

     
  3. 23.

    Click the Debug menu.

     
  4. 24.

    Choose Start Without Debugging.

     
The console window will appear as shown in Figure 14-13, and the card fee for each of the three countries based on the business logic implemented in each of the three country classes is displayed.

A screenshot of the console window depicts the card fee for each country. The credit card transaction fees equals 2% of 100 = 2.00, 2.5% of 100 = 2.50, and 3% of 100 = 3.00.

Figure 14-13

Card fee for all countries calculated

  1. 25.

    Press the Enter key to close the console window.

     

This is fantastic! We have coded a nice example with two interfaces containing interface methods, and we have three different classes that implemented the interfaces and their interface methods.

As we conclude this chapter on abstract classes and interfaces, let us think about the manager concept again. The manager, the abstract class or interface, dictates what they require through their abstract methods; and the employees, the concrete classes, implement all the abstract methods by making them concrete. Brilliant!

Yes, the idea of an abstract class or interface is great, and we have seen that they work well in code. BUT what happens if CountryTwo does not have credit cards, only debit cards? Well, one solution would be that the CountryTwo class will not have an if construct and simply call the DebitCardPaymentFee() method, as in Listing 14-51. The CreditCardPaymentFee() method could be left as it is or changed to, for example, return 0.00 – after all, it is never called. The problem with this “fix” is that whether it is a credit or debit transaction, the debit fee is charged, so all in all a poor development solution. But it is here to illustrate that a problem has been found because this country code needs to be different.
public double PaymentMethod(String paymentType, double transactionAmount)
    {
      double cardFee;
      cardFee = DebitCardPaymentFee(transactionAmount);
      return cardFee;
    } // End of PaymentMethod() method
Listing 14-51

CountryTwo class with no if construct

So the real issue is that because the “manager,” the interface, has three methods and developer 2 is told to inherit the IEcommercePayment interface, they must implement all three methods, even though they will not be using one or more of the methods. It's a contract that cannot be broken.

But did we not read in Chapter 8 the following?

There is a programming concept known as YAGNI, which stands for You Ain't Going To Need It.

Yes, we did read this. So how does the interface and class design we have just suggested for CountryTwo fit with this concept? It doesn't is the simple answer. Now, how can we get around the YAGNI for developer 2 and their country 2 class? It is a strange but simplistic solution in which we develop interfaces with only one interface method and the implementing classes can then choose which interfaces they wish to implement. For developer 2 and their CountryTwo 2, they might follow a process similar to the following example.

Add a new folder to the Chapter14 project to hold the code for this example.
  1. 1.

    Right-click the solution Chapter14 project name.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Folder.

     
  4. 4.

    Name the folder Example4Interfaces.

     

IEcommerceBilling Interface

We will now add a new interface, which will have only one interface method. As we do this example, we can copy and paste the relevant code from the existing interface from the Example3Interfaces project and simply change references from Example3 to Example4.
  1. 5.

    Right-click the Example4Interfaces folder.

     
  2. 6.

    Choose Add.

     
  3. 7.

    Choose New Item.

     
  4. 8.

    Choose Interface.

     
  5. 9.

    Name the interface IEcommerceBilling.cs .

     
  6. 10.

    Click the Add button.

     
The IEcommerceBilling.cs class code will appear in the editor window.
  1. 11.

    Amend the code to have the one interface method, as in Listing 14-52.

     
namespace Chapter14.Example4Interfaces
{
  internal interface IEcommerceBilling
  {
    // interface method
    double TaxCalculation(double itemPrice);
  } // End of IEcommerceBilling interface
} // End of Chapter14.Example4Interfaces namespace
Listing 14-52

Interface with only one interface method

IPaymentMethod Interface

Add a new interface for the payment method, which has only one interface method.
  1. 1.

    Right-click the Example4Interfaces folder.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Item.

     
  4. 4.

    Choose Interface.

     
  5. 5.

    Name the interface IPaymentMethod.cs .

     
  6. 6.

    Click the Add button.

     
The IPaymentMethod.cs class code will appear in the editor window.
  1. 7.

    Amend the code to have the one interface method, as in Listing 14-53.

     
namespace Chapter14.Example4Interfaces
{
  internal interface IPaymentMethod
  {
    // Interface methods
    double PaymentMethod(String paymentType, double transactionAmount);
  } // End of IPaymentMethod interface
} // End of Chapter14.Example4Interfaces namespace
Listing 14-53

Interface with only one interface method

IDebitCardPayment Interface

Add a new interface for the debit card payment method, which has only one interface method.
  1. 8.

    Right-click the Example4Interfaces folder.

     
  2. 9.

    Choose Add.

     
  3. 10.

    Choose New Item.

     
  4. 11.

    Choose Interface.

     
  5. 12.

    Name the interface IDebitCardPayment.cs .

     
  6. 13.

    Click the Add button.

     
The IDebitCardPayment.cs class code will appear in the editor window.
  1. 14.

    Amend the code to have the one interface method, as in Listing 14-54.

     
namespace Chapter14.Example4Interfaces
{
  internal interface IDebitCardPayment
  {
    // Interface methods
    double DebitCardPaymentFee(double debitAmount);
  } // End of IDebitCardPayment interface
} // End of Chapter14.Example4Interfaces namespace
Listing 14-54

Interface with only one interface method

ICreditCardPayment Interface

Add a new interface for the credit card payment method, which has only one interface method.
  1. 15.

    Right-click the Example4Interfaces folder.

     
  2. 16.

    Choose Add.

     
  3. 17.

    Choose New Item.

     
  4. 18.

    Choose Interface.

     
  5. 19.

    Name the interface ICreditCardPayment.cs .

     
  6. 20.

    Click the Add button.

     
The ICreditCardPayment.cs class code will appear in the editor window.
  1. 21.

    Amend the code to have the one interface method, as in Listing 14-55.

     
namespace Chapter14.Example4Interfaces
{
  internal interface ICreditCardPayment
  {
    // interface method
    double CreditCardPaymentFee(double creditAmount);
  } // End of ICreditCardPayment interface
} // End of Chapter14.Example4Interfaces namespace
Listing 14-55

Interface with only one interface method

Now we have four interfaces, each with only one abstract method, and our concrete classes can now select which of the interfaces they wish to use. This will mean there is no need for a concrete class to have methods that it will not use. No more YAGNI.

Code the class CountryTwo.
  1. 22.

    Right-click the Example4Interfaces folder.

     
  2. 23.

    Choose Add.

     
  3. 24.

    Choose Class.

     
  4. 25.

    Name the class CountryTwo.cs .

     
  5. 26.

    Click the Add button.

     
This country does not have credit card payments, so it does not need to inherit the interface ICreditCardPayment. This concrete class will implement the abstract methods by adding code to the methods, making them concrete methods.
  1. 27.

    Amend the code to implement the interface methods for the three interfaces that this class inherits from, as in Listing 14-56.

     
namespace Chapter14.Example4Interfaces
{
  internal class CountryTwo : IEcommerceBilling,
                            IPaymentMethod, IDebitCardPayment
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.15;
    } // End of TaxCalculation() method
    public double PaymentMethod(String paymentType,
                                    double transactionAmount)
    {
      double cardFee;
      cardFee = DebitCardPaymentFee(transactionAmount);
      return cardFee;
    } // End of PaymentMethod() method
    public double DebitCardPaymentFee(double debitAmount)
    {
      return debitAmount * 0.02; // 2%
    } // End of DebitCardPaymentFee() method
  } // End of CountryTwo class
} // End of Chapter14.Example4Interfaces namespace
Listing 14-56

Implement the methods

  1. 28.

    Right-click the Example4Interfaces folder.

     
  2. 29.

    Choose Add.

     
  3. 30.

    Choose Class.

     
  4. 31.

    Name the class as EcommerceApplication.cs.

     
We will now add the class containing the Main() method and call the PaymentMethod() method in the CountryTwo concrete class, passing it a debit card and an amount of 100, and have it display the returned fee amount. Before we run the application code, we should know what we expect as an answer. When using a debit card in this country, there is a 2% fee, so we should expect to get back 2% of 100, which is 2.
  1. 32.

    Amend the EcommerceApplication class code, as in Listing 14-57.

     
namespace Chapter14.Example4Interfaces
{
  internal class EcommerceApplication
  {
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      double feeForUsingACard;
      CountryTwo myCountryTwo = new CountryTwo();
      Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryTwo.TaxCalculation(itemPrice)}");
      feeForUsingACard = myCountryTwo.PaymentMethod("Debit", itemPrice);
      Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
    } // End of Main() method
  } // End of EcommerceApplication class
} // End of Chapter14.Example4Interfaces namespace
Listing 14-57

Display the card fee

  1. 33.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 34.

    Choose Properties from the pop-up menu.

     
  3. 35.

    Choose the Chapter14.Example4Interfaces.EcommerceApplication class in the Startup object drop-down list, as shown in Figure 14-14.

     

A window depicts a startup object dropdown menu to change the startup class.

Figure 14-14

Changing the startup class in the C# project

A screenshot of a console window in which the calculated credit card transaction fee for country 2 is displayed. The text reads, credit card transaction fee equals 2 percent of 100 equals 2.

Figure 14-15

Card fee for country 2 calculated

  1. 36.

    Click the File menu.

     
  2. 37.

    Choose Save All.

     
  3. 38.

    Click the Debug menu.

     
  4. 39.

    Choose Start Without Debugging.

     

The console window will appear as Figure 14-15, and show the card fee of 2.00, which we know is correct, because we did the calculation before we ran the code and knew what the expected outcome should be. This 2.00 result is the same result as we got from the previous code when we had not separated the abstract methods, as shown in Figure 14-13, but now we have segregated our abstract methods in different interfaces.

  1. 40.

    Press the Enter key to close the console window.

     
Now we have a “solution”, but in reality if we were to pass Credit to the CountryTwo PaymentMethod() method, we would get the same result, which would not be what we wanted. The correct solution would be to only have the IDebitCardPayment and ICreditCardPayment interfaces and forget the IPaymentMethod interface. Then CountryTwo only implements the IDebitCardPayment and cannot get access to the ICreditCardPayment.
  1. 41.

    Right-click the CountryTwo.cs file in the Example4Interfaces folder and choose Copy.

     
  2. 42.

    Right-click the Example4Interfaces folder and choose paste.

     
  3. 43.

    Rename the CountryTwo – Copy.cs file to CountryTwoDebit.cs.

     
  4. 44.

    Amend the CountryTwoDebit.cs to remove the implementation of the IPaymentMethod as in Listing 14-58.

     
namespace Chapter14.Example4Interfaces
{
  internal class CountryTwoDebit : IEcommerceBilling, IDebitCardPayment
  {
    public double TaxCalculation(double itemPrice)
    {
      return itemPrice * 0.15;
    } // End of TaxCalculation() method
    public double DebitCardPaymentFee(double debitAmount)
    {
      return debitAmount * 0.02; // 2%
    } // End of DebitCardPaymentFee() method
  } // End of CountryTwoDebit class
} // End of Chapter14.Example4Interfaces namespace
Listing 14-58

New CountryTwo class not implementing IPaymentMethod

  1. 45.

    Amend the EcommerceApplication.cs code to add an additional variable called feeForUsingADebitCard, instantiate the new class, and call the method that calculates the fee, assigning the returned value to the variable we have created, as in Listing 14-59.

     
namespace Chapter14.Example4Interfaces
{
  internal class EcommerceApplication
  {
    static void Main(string[] args)
    {
      double itemPrice = 100.00;
      double feeForUsingACard, feeForUsingADebitCard;
      CountryTwo myCountryTwo = new CountryTwo();
      Console.WriteLine($"The tax on an item of price £{itemPrice} is £{myCountryTwo.TaxCalculation(itemPrice)}");
      feeForUsingACard = myCountryTwo.PaymentMethod("Debit", itemPrice);
      Console.WriteLine($"The fee for using this card with this transaction amount is £{feeForUsingACard: 0.00}");
      CountryTwoDebit myCountryTwoDebit = new CountryTwoDebit();
      feeForUsingADebitCard = myCountryTwoDebit.DebitCardPaymentFee(itemPrice);
      Console.WriteLine($"The fee for using a debit card with this transaction amount is £{feeForUsingADebitCard: 0.00}");
    } // End of Main() method
  } // End of EcommerceApplication class
} // End of Chapter14.Example4Interfaces namespace
Listing 14-59

Instantiate the CountryTwoDebit class and find card fee

  1. 46.

    Click the File menu.

     
  2. 47.

    Choose Save All.

     
  3. 48.

    Click the Debug menu.

     
  4. 49.

    Choose Start Without Debugging.

     
The console window will display the result as in Figure 14-16.

A screenshot of a console window in which a debit card transaction fee is displayed. The text reads, debit card transaction fee equals 2 percent of 100 equals 2. The same result can also be obtained from the I debit card payment implementation.

Figure 14-16

Card fee for CountryTwo calculated

  1. 50.

    Press the Enter key to close the console window.

     

We can now see that using segregation in our interfaces follows the same principle we read about when we dealt with methods and classes. In following this principle of segregation, we are writing clean code, avoiding issues around YAGNI, using the SOLID principles of interface segregation and single responsibility, and using another industry principle of programming to an interface.

Should we use an abstract class or an interface?

Whether we use an abstract class or an interface will depend on the needs we have or the application we are coding. As we have seen, abstract classes and interfaces are similar, but as we have seen there are differences , and it will be these differences that help us decide which is the best option. We need to consider a few things:
  • An “abstract class” can have abstract methods and concrete methods.
    • We have seen that an abstract class contains abstract methods, but it can also contain concrete methods, therefore allowing classes that inherit from the abstract class to override or implement the methods.

    • On the other hand, we saw that an interface cannot contain concrete methods.

    Do we want our class to implement from more than one “abstract class”?
    • We have seen that a class can inherit only one abstract class.

    • On the other hand, a class can inherit more than one interface – multiple inheritance.

But whoa! What did we read earlier?

With C# 8 we can now add concrete methods, methods with code. They are called default methods.

Eve though we have a default method in the interface any class that inherits from the interface can choose to use the default method, amend the method, or ignore it. We will now verify that it is possible to add a default method and see how this is implemented, or not, in a class.

Concept of Default Method in an Interface

We will now add a new folder to the project to hold the code for this example where we will code the interfaces with the default method .
  1. 1.

    Right-click the solution Chapter14 project name.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Folder.

     
  4. 4.

    Name the folder Example5Interfaces.

     

IPolicy Interface

Now we will add a new interface containing interface methods.
  1. 5.

    Right-click the Example5Interfaces folder.

     
  2. 6.

    Choose Add.

     
  3. 7.

    Choose New Item.

     
  4. 8.

    Choose Interface.

     
  5. 9.

    Name the interface IPolicy.cs.

     
  6. 10.

    Click the Add button.

     
  7. 11.

    Amend the code to add the abstract methods and a default method, as in Listing 14-60.

     
namespace Chapter14.Example5Interfaces
{
  internal interface IPolicy
  {
    // abstract methods, method signature and return type
    void CreateAPolicy();
    void CloseAPolicy();
    // C# 8 allows us to have default implementations
    public void Print(string policyName)
    {
      Console.WriteLine($"The policy type created by the " +
        $"default interface implementation is {policyName}");
        }
  } // End of IPolicy interface
} // End of Chapter14.Example5Interfaces namespace
Listing 14-60

Interface with abstract methods and a default method

PolicyManager Class Inheriting from the IPolicy Interface
  1. 12.

    Right-click the Example5Interfaces folder.

     
  2. 13.

    Choose Add.

     
  3. 14.

    Choose Class.

     
  4. 15.

    Name the class PolicyManager.cs.

     
  5. 16.

    Click the Add button.

     
We will now have this PolicyManager class implement the IPolicy interface and make the abstract methods of the interface concrete, thereby ensuring the “contract” with the interface is applied.
  1. 17.

    Amend the code, as in Listing 14-61.

     
namespace Chapter14.Example5Interfaces
{
internal class PolicyManager: IPolicy
{
// Implement the abstract method, make it a concrete method
public void CreateAPolicy()
{
        Console.WriteLine("Policy created");
}  // End of CreateAPolicy() concrete method
// Implement the abstract method, make it a concrete method
public void CloseAPolicy()
{
        Console.WriteLine("Policy closed");
} // End of CloseAPolicy() concrete method
}//End of PolicyManager class that implements IPolicy interface
} // End of Chapter14.Example5Interfaces
Listing 14-61

Concrete class implementing the abstract methods

Program Class with the Main() Method
  1. 18.

    Right-click the Example5Interfaces folder.

     
  2. 19.

    Choose Add.

     
  3. 20.

    Choose Class.

     
  4. 21.

    Name the class PolicyApplication.cs .

     
  5. 22.

    Click the Add button.

     
  6. 23.

    Amend the code to instantiate the PolicyManager class and call the concrete methods that we created in it as in Listing 14-62.

     
namespace Chapter14.Example5Interfaces
{
  internal class PolicyApplication
  {
  public static void Main()
  {
    Console.WriteLine("C# 8 default methods in an Interface");
 // Instantiate the PolicyManager class
    IPolicy myPolicyManager = new PolicyManager();
 // Call the CreateAPolicy method from the PolicyManager instance
    myPolicyManager.CreateAPolicy();
 // Call the CloseAPolicy method from the PolicyManager instance
    myPolicyManager.CloseAPolicy();
 // Call the default method from the PolicyManager instance
    myPolicyManager.Print("Auto");
  } // End of Main() method
  } // End of PolicyApplication class
} // End of Chapter14.Example5Interfaces class
Listing 14-62

Main class , which uses the default method of the interface

  1. 24.

    Right-click the Chapter14 project in the Solution Explorer panel.

     
  2. 25.

    Choose Properties from the pop-up menu.

     
  3. 26.

    Choose the Chapter14.Example5Interfaces.PolicyApplication class in the Startup object drop-down list, as shown in Figure 14-17.

     

A screenshot of the console window depicts the startup object dropdown menu to set the startup project.

Figure 14-17

Set the startup project

  1. 27.

    Click the File menu.

     
  2. 28.

    Choose Save All.

     
  3. 29.

    Click the Debug menu.

     
  4. 30.

    Choose Start Without Debugging.

     
The console window will appear, as shown in Figure 14-18, and display the message from the default method of the interface.

A screenshot of the console window depicts the execution of the default method of the interface.

Figure 14-18

Default method of the interface has been executed

  1. 31.

    Press the Enter key to close the console window.

     

The code has worked, and in the code line

IPolicy myPolicyManager = new PolicyManager();

we have used the interface IPolicy when instantiating, but what would happen if we were to use the PolicyManager class , which inherits from the IPolicy interface, and our code line would be

PolicyManager myPolicyManager = new PolicyManager

Well, let us see how this works out.
  1. 32.

    Amend the code to use PolicyManager rather than IPolicy, as shown in in Listing 14-63.

     
  public static void Main()
  {
    Console.WriteLine("C# 8 default methods in an Interface");
    // Instantiate the PolicyManager class
    PolicyManager myPolicyManager = new PolicyManager();
Listing 14-63

Instantiate using the class rather than the interface

We will see that we have a compile error in the amended code.
  1. 33.

    Hover over the red underline of the Print("Auto") in the line of code myPolicyManager.Print() as shown in Figure 14-19.

     

A screenshot highlights the error message. The print method is not available.

Figure 14-19

Error, method not available

The compile error tells us that the myPolicyManager object does not contain a Print() method. This is telling us that the default method is not accessible; in other words, the inherited class knows nothing about the default method of the interface. To make it accessible, we must use the interface, and we can achieve this in two different ways:
  • We can go back to using the IPolicy in the instantiation:

// Instantiate the PolicyManager class
IPolicy myPolicyManager = new PolicyManager();
  • We can upcast the myPolicyManager to an interface:

// Call the default method from the PolicyManager instance
 ((IPolicy)myPolicyManager).Print("Auto");
We will use the upcasting technique since we have not used this before with an interface. Upcasting follows the same principle as we used when casting with our data types. When we looked at casting in Chapter 7, we said

In C#, casting is a method used to convert one data type to another. Casting is used as an explicit conversion and tells the compiler what to do.

  1. 34.

    Amend the code within the Main() method to use the upcasting, as shown in Listing 14-64.

     
// Call the CloseAPolicy method from the PolicyManager instance
      myPolicyManager.CloseAPolicy();
// Call the default method from the PolicyManager instance
      ((IPolicy)myPolicyManager).Print("Auto");
    } // End of Main() method
  } // End of PolicyApplication class
} // End of Chapter14.Example5Interfaces class
Listing 14-64

Upcast the class to an interface

  1. 35.

    Amend the code in the PolicyManager class to add an “overridden” version of the interface default method, as in Listing 14-65.

     
public void CloseAPolicy()
{
  Console.WriteLine("Policy closed");
} // End of CloseAPolicy() concrete method
/*
C# 8 allows us to have default implementations
*/
public void Print(string policyName)
{
 Console.WriteLine($"The policy type created by the overridden default interface method is {policyName }");
}
}//End of PolicyManager class that implements IPolicy interface
} // End of Chapter14.Example5Interfaces
Listing 14-65

Override the default method of the interface

  1. 36.

    Click the File menu.

     
  2. 37.

    Choose Save All.

     
  3. 38.

    Click the Debug menu.

     
  4. 39.

    Choose Start Without Debugging.

     
The console window will appear and show the message from the default method of the interface, as in Figure 14-20.

A screenshot of the console window depicts the overridden version of the default method.

Figure 14-20

Default method has been overridden

  1. 40.

    Press the Enter key to close the console window.

     

We have seen that we can have a default implementation in our interface, but the class that implements the interface does not have to implement the default method – it is optional. When we talk about “program to an interface” as a design approach, we use the interface as our starting point. In using such a design approach, we will then have many classes that are dependent on the interface, and if we then decide to go back and amend the interface, we will impact all the dependent classes. So we would say that once the interface is designed, it is not open for amendments. However, as we have seen, C# 8 allows us to add default implementations to the interface, and this does not break any of the existing classes that implement the interface, since the default methods are optional. We therefore say that with C# 8 and above, the interface is expandable in terms of adding default methods.

Concept of Static Methods and Fields in an Interface

With C# 8 interfaces, we were introduced to another new feature in which we can have static members, methods, and fields . We read earlier the following:

Remember that static means belonging to the class or Interface.

So not only can a class have static members but an interface, from C# 8, can also have static members. We will now add a static member, field, to the IPolicy interface.
  1. 41.

    Amend the code, as in Listing 14-66, to declare a static field to the IPolicy interface that will be used to record the number of current policies.

     
using System;
namespace Chapter14.Example5Interfaces
{
  internal interface IPolicy
  {
    /*
    C# 8 allows us to have static members. Here we use a
    static field.
    Remember that static means belonging to the class or
    Interface.
    We can therefore call the methods and members of the
    interface directly.
    */
    static int policyCounter;
Listing 14-66

Static field in an interface

From within the Main() method, we will now call the policyCounter field directly from its location in the interface. We will call the policyCounter field twice, once when the policy is created and again when the policy is closed. The static nature of the field means there is only one version of the field; there is no copy made when the class is instantiated.
  1. 42.

    Amend the PolicyApplication.cs class code, as in Listing 14-67.

     
using System;
namespace Chapter14.Example5Interfaces
{
  internal class PolicyApplication
  {
    public static void Main()
    {
      Console.WriteLine("C# 8 default methods in an Interface");
      // Instantiate the PolicyManager class
      PolicyManager myPolicyManager = new PolicyManager();
 // Call the CreateAPolicy method from the PolicyManager instance
      myPolicyManager.CreateAPolicy();
      // Increment the static policyCounter by 1
      IPolicy.policyCounter = IPolicy.policyCounter + 1;
      Console.WriteLine($"Policy created - there are now {IPolicy.policyCounter} policies");
 // Call the CloseAPolicy method from the PolicyManager instance
      myPolicyManager.CloseAPolicy();
      // Decrement the static policyCounter by 1
      IPolicy.policyCounter = IPolicy.policyCounter - 1;
      Console.WriteLine($"Policy closed - there are now {IPolicy.policyCounter} policies");
// Call the default method from the PolicyManager instance
      ((IPolicy)myPolicyManager).Print("Auto");
    } // End of Main() method
  } // End of Program class
} // End of Chapter14.Example5Interfaces class
Listing 14-67

Call the static field in an interface

  1. 43.

    Click the File menu.

     
  2. 44.

    Choose Save All.

     
  3. 45.

    Click the Debug menu.

     
  4. 46.

    Choose Start Without Debugging.

     
The console window will appear as shown in Figure 14-21. The console message shows that after the CreateAPolicy() method is called, we have one policy. Then the message shows that there are zero policies after the CloseAPolicy() method is called.

A screenshot of the console window depicts the static field of the interface.

Figure 14-21

Static field of the interface has been called.

Chapter Summary

In this chapter we have tackled two large concepts, abstract classes and interfaces, and we have looked at the differences and similarities between them. We have also seen that since C# 8 the concept of an interface changed from being a template that could only have abstract methods to being capable of having default implementations with business logic. When creating a class that inherits from the interface with the default implementation, we do not have to use the default implementation, but if we do use it, we call it directly from the interface. In the previous chapter, we looked at classes and objects, and now we should appreciate that abstract classes and interfaces sit alongside classes. A class can decide to form a contract with an abstract class or interface, and then it will have to implement the abstract methods that are part of the contract. This has been a “big chapter” in terms of learning, but we are now really moving to coding at a higher level and seeing how applications are coded in the commercial world.

We are making fantastic progress in our programming of C# applications and we should be very proud of our achievements. In finishing this very important chapter, we have increased our knowledge further and we are advancing to our target.

An illustration of a concentric circle depicts the advancement of the target. The text at the bottom reads our target is getting closer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.61.187