As mentioned earlier, by deriving from an interface, a class is declaring that it implements certain functions. Because not all object-oriented languages support interfaces, this section examines C#’s implementation of interfaces in detail.
Developers familiar with COM should be aware that, although, conceptually, C# interfaces are similar to COM interfaces, they are not the same thing. The underlying architecture is different. For example, C# interfaces are not derived from IUnknown.A C# interface provides a contract stated in terms of .NET [unctions. Unlike a COM interface, a C# interface does not represent any kind of binary standard.
This section illustrates interfaces by presenting the complete definition of one of the interfaces that has been predefined by Microsoft, System. IDisposable. IDisposable contains one method, Dispose (;, which is intended to be implemented by classes to clean up code:
public interface IDisposable
This code shows that declaring an interface works syntactically in pretty much the same way as declaring an abstract class. You should be aware, however, that it is not permitted to supply implementations of any of the members of an interface. In general, an interface can only contain declarations of methods, properties, indexers, and events.
You can never jnstantiate an interface; it contains only the signatures of its members. An interface has neither constructors (how can you construct something that you can’t instantiate?) nor fields (because that would imply some internal implementation). An interface definition is also not allowed to contain operator overloads, although that’s not because there is any problem in principle with declaring them- ” there isn’t; it is because interfaces are usually intended to be public contracts, and having operator . overloads would cause some incompatibility problems with other .NET languages, such as Visual Basic .NET, which do not support operator overloading.
It is also not permitted to declare modifiers on the members in an interface definition. Interface members are always implicitly public, and cannot be declared as virtual or static. That’s up to implementing classes to decide. It is therefore fine for implementing classes to declare access modifiers, as is done in the example in this section.
Take for example Disposable. If a class wants to declare publicly that it implements the Dispose ( ) method, it must implement Disposable – which in C# terms means that the class derives from IDisposable..
In this example, if SomeClass derives from IDisposable but doesn’t contain a Dispose ( ) implementation with the exact same signature as defined in IDisposable, you get a compilation error because the class would be breaking its agreed-on contract to implement IDisposable, Of course, there’s no problem for the compiler about a class having a Dispose () method but not deriving from IDisposable. The problem, then, would be that other code would have no way of recognizing that SomeClass has agreed to support the IDisposable features.
IDisposable is a relatively simple interface because it defines only one method, Most interfaces will contain more members.
Another good example of an interface is provided by the foreach loop in C#, In principle, the foreach loop works internally by querying the object to find out whether it implements an interface called System. Collections. IEnumerable. If it does, theCs compiler will inject IL code, which uses the methods on this interface to iterate through the members of the collection. If it doesn’t, foreach will raise an exception. It’s worth pointing out that both IEnumerable and IDisposable are somewhat special interfaces to the extent that they are actually recognized by the C# c,pmpiler, which takes account of these interfaces in the code that it generates. Obviously-any interfaces that you define yourself won’t be so privileged!
Defining and Implementing Interfaces
This section illustrates how to define and use interfaces through developing a short program that follows the interface inheritance paradigm. The example is based on bank accounts. Assume that you are writing code that will ultimately allow computerized transfers between bank accounts. And assume for this example that there are many companies that may implement bank accounts, but they have all mutually agreed that any classes that represent bank accounts will implement an interface, I Bank Account, which exposes methods to deposit or withdraw money, and a property to return the balance. It is this interface that will allow outside code to recognize the various bank account classes implemented by different bank accounts. Although the aim is to allow the bank accounts to talk to each other to allow transfers of funds between accounts, we won’t introduce that feature just yet.
To keep things simple, you will keep all the code for the example in the same source file. Of course, if something like the example were used in real life, you could surmise that the different bank account classes would not only be ‘compiled to different assemblies, but would also be hosted on different machinesowned by the different banks. That’s all much too complicated for our purposes here. However, to maintain some attempt at realism, you will define different namespaces for the different companies.
To begin, you need to define the IBank Account interface:
public interface IBankAccount
void Payln(decimal amount);
bool Withdraw(decimal amount);
Notice the name of the interface, IBankAccount. It’s a convention that an interface name traditionally starts with the letter I, so that you know that it’s an interface.
The idea is that you can now write classes that represent bank accounts. These classes don’t have to be related to each other in any way; they can be completely different classes. They will, however, all declare that they represent bank accounts by the mere fact that they implement the IBankAccount interface.
Let’s start off with the first class.a saver account run by the Royal Bank of Venus:
It should be pretty obvious what the implementation of this class does. You maintain a private field, balance, and adjust this amount when money is deposited or withdrawn. You display an error message if an attempt to withdraw money fails because there is insufficient money in the account. Notice also that, because we want to keep the code as·simple as possible, you are not implementing extra properties, such as the account holder’s name! In real life that would be pretty essential information, but for this example it’s unnecessarily complicated.
The only really interesting line in this code is the class declaration:
public class SaverAccount : IBankAccount
You’ve declared thatsaverxccount is derived from one interface, IBankAccount, and you have not explicitly indicated any other base classes (which of course means thilt SaverAccount is derived directly from System. Obj ect). By the way, derivation from interfaces acts completely independently from derivation from classes.
Being derived from IBankAccount means that SaverAccount gets all the members of IBankAccount. But because an interface doesn’t actually implement any of its methods, SaverAccount must provide its own implementations of all of them. If any implementations are missing, you can rest assured that the compiler will complain. Recall also that the interface just indicates the presence of its members. It’s up to the class to decide if it wants any of them to be virtual or abstract (though abstract functions are of course only allowed if the class itself is abstract). For this particular example, you don’t have any reason to make any of the interface functions virtual.
To illustrate how different classes can implement the same interface, assume that the Planetary Bank of Jupiter also implements a class to represent one of its bank accounts – a Gold Account:
public class GoldAccount : IBankAccount
We won’t present details of the Gold Account class here; in the sample code, it’s basically identical to the implementation of Saver Account. We stress that Gold Account has no connection with Saver Account, other than that both happen to implement the same interface.
Now that you have your classes, you can test them out. You first need a couple of using statements:
This code (which if you download the sample, you can find in the file BankAccounts. cs) produces this output:
The main point to notice about this code is the way that you have declared both your reference variables as IBankAccount references. This means that they can point to any instance of any class that implements this interface. However, it also means that you can call only methods that are part of this interface through these references – if you want to call any methods implemented by a class that are not part of the interface, you need to cast the reference to the appropriate type. In the example code, you were able to call ToString () (not implemented by IBankAccount) without any explicit cast, purely because ToString () is a System. Object method, so the C# compiler knows that it will be supported by any class and Casts,” covers the syntax for how to perform casts.
Interface references can in all respects be treated like class references – but the power of an interface reference is that it can refer to any class that implements that interface. For example, this allows you to form arrays of interfaces, where each element of the array is a different class:
It’s possible for interfaces to inherit from each other in the same way that classes do. This concept is illustrated by defining a new interface, ITransferBankAccount, which has the same features as IBankAccount but also defines a method to transfer money directly to a different account:
Because ITransferBankAccount is derived from IBankAccount, it gets all the members of IBankAccount as well as its own. That means that any class that implements (derives from) ITransferBankAccount must implement all the methods of IBankAccount, as well as the new TransferTo () method defined in ITransferBankAccount. Failure to implement all of these methods will result in a compilation error.
Note that the TransferTo () method uses an IBankAccount interface reference for the destination account. This illustrates the usefulness of interfaces: when implementing and then invoking this method, you don’t need to know anything about what type of object you are transferring money to – all you need to know is that this object implements IBankAccount.
To illustrate ITransferBankAccount, assume that the Planetary Bank ofJupiter also offers a current account. Most of the implementation of the CurrentAccount class is identical to the implementations of SaverAccount and GoldAccount (again, this is just to keep this example simple – that won’t normally be the case), so in the following code just the differences are highlighted:
The class can be demonstrated with this code:
This code (CurrentAccount. cs) produces the following output. which, as you can verify. shows that the correct amounts have been transferred:
This chapter examined how to code inheritance in C#. You have seen that C# offers rich support for both multiple interface and single implementation inheritance. You have also learned that C# provides a number of useful syntactical constructs designed to assist in making code more robust, such as the override keyword. which indicates when a function should override a base function; the new keyword. which indicates when a function hides a base function; and rigid rules for constructor initializers that are designed to ensure that constructors are designed to interoperate in a robust manner.