Unlike syntactic differences, which reflect merely a difference in keywords and format, object-oriented differences between C# and VB stem from differences in implementation between the two languages.
Both C# and VB can use class-based inheritance. In .NET, a class can derive from at most one other class; in other words, the system only allows single inheritance. However, a class can implement multiple interfaces. Both languages have different semantics for class inheritance and interface implementation. (Both require, though, that the inherited class be listed before any implemented interfaces.)
To use class-based inheritance in C#, list the class you would like to inherit from immediately after the derived class name, separated by a colon, as in the following example:
class Account{}class Checking : Account{}
In this example, the Checking class derives from the Account class.
Class-based inheritance in VB is done with the Inherits keyword:
Class AccountEnd Class Class Checking Inherits AccountEnd Class
Often, a colon is used to combine the declaration for the class with the Inherits statement to make the line of code look more C#-like:
Class Checking : Inherits AccountEnd Class
Using Inherits in this fashion is the same as moving the Inherits clause to the next line without a colon.
Overloading means that several methods have the same name but different signatures. Method overloading differs slightly between the languages. Method overloading in C# and in VB.NET does not require a keyword; you simply add another method with the same name, as in the following example:
class Account{ public void MakeDeposit(int Amount) { } public void MakeDeposit(int Amount, int AmountAvailable) { }}
In this example, the Account class has two methods named MakeDeposit. The first method accepts only one integer value, and the second method accepts two integer values. The main thing to remember when overloading methods is that the compiler must know at compile time what version of the function to call. In this simple example, it is easy to see what version of the MakeDeposit method the compiler will call: it depends on the number of parameters. As you will see in a moment, it is not always easy to figure out what version will be invoked.
Method overloading in VB is done in the same fashion. The following example illustrates basic method overloading in VB:
Class Account Sub MakeDeposit(ByVal Amount As Integer) End Sub Sub MakeDeposit(ByVal Amount As Integer, _ ByVal AmountAvailable As Integer) End SubEnd Class
C# enables you to overload methods based on the direction of parameters. For example:
class Checking{ public void MakeDeposit(int Amount) { } public void MakeDeposit(ref int Amount) { }}
The first MakeDeposit has a byval parameter, while the second has a byref parameter of the same type. This is a legal way of overloading methods in C# because, when you invoke a method that has ref parameters, you must use the ref keyword in the invocation as follows:
Checking check = new Checking( );int Amount = 10;//calls the version with a byval parametercheck.MakeDeposit(Amount);//calls the version with a byref parametercheck.MakeDeposit(ref Amount);
In VB.NET, however, you cannot overload based on the direction of the parameters. This is because you do not have to use a keyword like ref to pass ByRef parameters to a function; how parameters are passed is defined by the method definition. Therefore, the compiler cannot distinguish between a call to a function that has a ByVal parameter and one that has a ByRef parameter of the same type.
VB.NET has an Overloads keyword that you can apply in front of overloaded method definitions, as follows:
Class Checking Overloads Sub MakeDeposit(ByVal Amount As Integer) End Sub Overloads Sub MakeDeposit(ByVal Amount As Long) End SubEnd Class
The Overloads keyword is used in front of the method declaration. However, this keyword is optional. It is required only if the class is overloading a method from a base class—if, for example, the Account class has a MakeDeposit method and Checking derives from Account and adds a second version of MakeDeposit. Overloads in a derived class tells the compiler to use hide-by-signature semantics rather than hide-by-name semantics. (See the upcoming Section 1.3.7 for a full explanation of when to use Overloads in a derived class.)
In C# it is illegal to add parameters to properties. In VB.NET, however, properties can have parameters. Therefore, it is possible to also overload properties in VB.NET. Here is an example:
Class Person ReadOnly Property Name( ) As String Get End Get End Property ReadOnly Property Name(ByVal Format As String) _ As String Get End Get End PropertyEnd Class
The Person class has two definitions for the Name property: one that has no input parameters, and one that accepts a string input parameter.
Both VB.NET and C# enable the developer to add one or multiple constructor functions—functions that trigger automatically when the program creates an instance of the class—to a class. However, the syntax in each language is different.
To declare a constructor in C#, use the name of the class, as in the following example:
class Account{ public Account( ) { } public Account(int Amount) { } internal Account(int Amount, int AmountAvail) { }}
This code shows the Account class with three constructors. The default constructor has no parameters. (If you do not add a constructor to your class, the C# compiler adds a default constructor automatically.) There are two other overloaded constructors. One is public and accepts one parameter, the other is marked as internal and accepts two parameters. Marking a constructor as internal means that only other classes within the same assembly may create the class while passing these two parameters.
VB also enables you to add constructors and overload constructors, and just like in C#, if you do not add a constructor, the compiler adds a default constructor automatically. In VB, constructors are declared as subroutines named New, as follows:
Class Account Sub New( ) End Sub Sub New(ByVal Amount As Integer) End Sub Friend Sub New(ByVal Amount As Integer, _ ByVal AmountAvail As Integer) End SubEnd Class
Just as in the C# example, the VB class contains three constructors. The class has a default constructor—the constructor that takes no parameters—and two overloaded constructors. One of the constructors is marked as Friend, which means that it is only visible by code in the same assembly.
Sometimes it is convenient for one constructor to forward code to another constructor. When the class has multiple constructors, sometimes you may choose to write all the code in one of the constructors and simply call the master constructor from other constructors. In C#, you can call a constructor by using the this keyword, as follows:
class Account{ int m_Balance; public Account( ) { //actual code goes here } public Account(int Amount) : this( ) { m_Balance = Amount; } }
Notice that the constructor definition uses a colon followed by the keyword this. The this keyword tells the compiler to write code to invoke another constructor in the class, in this case one that does not have any parameters. Another possibility is to forward the default constructor to the constructor that accepts one parameter, as follows:
class Account{ int m_Balance; public Account( ) : this(100) { } public Account(int Amount) { //actual code goes here } }
In this case the compiler writes code so that when the default constructor is used, the first thing the function does is forward the call to the constructor that accepts one parameter.
VB also has this capability. In VB, another constructor is invoked with the Me or MyClass keyword, as follows:
Class Account Dim m_Balance As Integer Sub New( ) 'actual code goes here End Sub Sub New(ByVal Amount As Integer) Me.New( ) m_Balance = Amount End SubEnd Class
The Me.New( ) call tells the compiler to forward the call to the constructor that takes no parameters. If you are forwarding a call to another constructor with Me.New( ), then the Me.New( ) line must come before any other line in the function; otherwise, the compiler issues an error. You may also use the MyClass keyword, as in MyClass.New( ). (MyClass will be explained in more detail later in Section 1.3.8.)
When you derive one class from another, sometimes it is necessary to forward the child's constructor to a parent constructor. Consider the following C# example:
class Account{ int m_Balance; public Account(int Amount) { m_Balance = Amount; }} class Checking : Account{}
With this just as it is at this point, the compiler would report an error because the Account class does not have a default constructor (a constructor that does not take parameters). When a client creates an instance of the Checking class, the runtime must invoke not only the constructor code for the Checking object, but also the constructor code for the base class, Account. It is the compiler that makes this happen. In this example, the compiler adds a default constructor to the Checking class (because the class has no constructors at the time). The first line in the constructor code that the compiler adds invokes the constructor for the base class. The latter's constructor then invokes the constructor of its base class, and so on. (If you add a constructor to your code, then the compiler adds a line to the constructor to invoke the base class constructor.) In the code above, however, the compiler is at a loss as to how to invoke the base class constructor. This is because the only constructor in the Account class accepts one parameter (and because the class has a constructor, the compiler does not add a default constructor). Thus, you have to add the code to invoke the base class's constructor yourself. The solution is the following:
class Checking : Account{ public Checking( ) : base(100) { }}
Notice from this example that when the base class does not have a default constructor, it is necessary to add an explicit constructor to the derived class, and more importantly, to write code to invoke a base constructor. To invoke the base constructor, you must use the base keyword as if it were a function name and pass the parameters that match the particular constructor.
VB has a similar construct, except that VB uses the MyBase keyword. As in C#, if the base class does not have a default constructor, the compiler generates an error, unless you add a constructor to the derived class and invoke the base class's constructor. The following example shows the VB equivalent:
Class Account Dim m_Balance As Integer Sub New(ByVal Amount As Integer) m_Balance = Amount End SubEnd Class Class Checking Inherits Account Sub New( ) MyBase.New(100) End SubEnd Class
Notice from this example that you must add a call to invoke the base class's constructor. To do this you must use the MyBase keyword. The keyword MyBase can be used to invoke any method in the base class. Unlike C#, in which you use the base keyword without specifying the name of a function, in VB you must use MyBase in combination with the New keyword. The VB compiler forces you to write the line MyBase.New( ) before any other code in the constructor.
Field initializers are class fields that are initialized in place, as in the following C# example:
class Account{ int Balance = 100; public void MakeDeposit( ) { }}
The line:
int Balance = 100
is a field initializer. This code does not have an explicit constructor function. However, if you do not provide one, the C# compiler adds a default constructor. It also adds code to this constructor to invoke the field initializers. In other words, when the code is compiled, the result should resemble the following:
class Account{ int Balance; public Account( ) { Balance = 100; //field initializers base( ); //call the base constructor //rest of the code } public void MakeDeposit( ) { }}
If your code contains multiple constructors, the compiler adds the code to set the field initializers to each constructor. The important thing to notice is that the C# compiler adds the initializing code to the beginning of the constructor function before calling the base constructor, and before calling any code you may have added to the constructor. The fact that the compiler adds the code for initializers before invoking the base constructor has a side effect: the this pointer in C# is not yet available at the time the field initializers are set. That means that your initializers in C# can only use constants or static fields, or invoke static (shared) functions.
In VB, things are different. VB also supports field initializers:
Class Account Dim Balance As Integer = 100 Sub MakeDeposit( ) End SubEnd Class
As in C#, the VB compiler adds a default constructor if one is not available. The VB compiler also adds code to each constructor to invoke the initializers. However, the order in which the initializers are invoked in relation to the base constructor is different in VB. The VB compiler turns the previous code into the following:
Class Account Dim Balance As Integer Sub MakeDeposit( ) End Sub Sub New( ) MyBase.New( ) 'first call the base 'constructor Balance = 100 'field initializers go 'after the call to the 'base constructor. 'the rest of the code goes here. End SubEnd Class
Because the initializers are set after the call to the base constructor, by the time the field initializers are invoked, the Me object is already populated. This means that in VB.NET, it is possible to have field initializers invoke instance methods. While in C# you can only invoke Shared (static) methods, in VB there is no such limitation.
What happens if a class inherits from another class and both classes have a member with the same name? If you are the author of the derived class, you may be careful not to add a member to your class that has the same name as a member in the base class. However, there is nothing stopping the author of the base class from adding a member with the same name as one of your members. Suppose you wrote a class to extend the Calculator class, and Calculator was missing a multiply method, so you added one to yours. Then, in the next release, the author of Calculator added a multiply method. Now what? Both C# and VB.NET do different things in this scenario. Let's look at how C# handles this. Consider the following code:
class Calculator{ public void Add( ) { } public void Subtract( ) { } public void Multiply( ) { }} class CalculatorEx : Calculator{ public void Multiply( ) { }}
CalculatorEx inherits from Calculator, and both classes have a method with the same name. If you try to compile this code, it compiles with a warning, not an error. The warning is:
The keyword new is required on'CalculatorEx.Multiply( )' because it hides inherited member 'Calculator.Multiply( )'
What the warning really means is that someone using a variable of type CalculatorEx will not be aware that there is a Multiply method in the base class. To the programmer, the only version of Multiply lives in the CalculatorEx class. However, if the programmer uses a reference to the base class type, he gets the base class version even if he creates an instance of the derived class. The following code illustrates this:
static void Main(string[] args){ Calculator calc1 = new CalculatorEx( ); calc1.Multiply( ); //calls base version CalculatorEx calc2 = new CalculatorEx( ); calc2.Multiply( ); //calls derived version}
As you can see, how you declare your variable will determine what version of Multiply is called. This behavior is called method hiding. There are two types of method hiding: hide-by-signature and hide-by-name. C# uses hide-by-signature. (Signature refers to the name of the function and its input parameters.) To illustrate the difference, imagine if Multiply in the base had different parameters from the one in the child:
class Calculator{ public void Multiply(int x, int y) { }} class CalculatorEx : Calculator{ public void Multiply(int x, int y, out int result) { result = x * y; }}
Now, it looks like the CalculatorEx class has two versions of Multiply: one that has two parameters, and one that has three parameters. It is as though the author were overloading the methods. Actually, building this code does not even generate a warning.
To complicate things, it is possible that a member in the base class is of a totally different type than the member in the derived type. For example:
class Calculator{ public bool Clear;}class CalculatorEx : Calculator{ public void Clear(int x,int y) { }}
In this example, Clear is a field in Calculator and a function in CalculatorEx. In this case, CalculatorEx does not have a combination of the field and the method, since it is impossible in the same class to have two members of a different type (a field and a function, for example) with the same name. Because of hiding, a programmer using CalculatorEx only sees Clear as a method.
When hiding occurs, you can suppress the compiler warning with the new keyword. Here is an example of applying the new keyword to the previous example:
class Calculator{ public bool Clear;} class CalculatorEx : Calculator{ public new void Clear(int x,int y) { }}
The new keyword is applied to the method. This tells the compiler, "I meant to do that." Of course, new is not just for suppressing the compiler warning; it also lets you hide instead of override a method when the base method is marked as virtual (see the next section, Section 1.3.8, for details).
VB.NET lets you choose between hide-by-name and hide-by-signature semantics. First, let's take a look at how VB does hide-by-name:
Class Calculator Function Multiply(ByVal x As Integer, _ ByVal y As Integer) As Integer End Function Function Multiply(ByVal x As Long, _ ByVal y As Long) As Long End FunctionEnd Class Class CalculatorEx Inherits Calculator Function Multiply(ByVal x As Integer, _ ByVal y As Integer, _ ByVal z As Integer) As Integer End FunctionEnd Class
This code compiles fine with only a warning. The warning reads:
function 'Multiply' shadows an overloadable member declared in the base class 'Calculator'. If you want to overload the base method, this method must be declared 'Overloads'.
The Calculator class has two versions of the Multiply method, one that accepts integer values and one that accepts longs. The CalculatorEx class has a single Multiply method that accepts three integer parameters. In theory, all these methods should be able to coexist in the same class. However, by default, VB uses hide-by-name semantics, which means that the programmer using CalculatorEx sees only one version of Multiply—the one in the CalculatorEx class. Any other members with the same name are hidden. This is what happens implicitly; however, VB has two keywords to control the hiding method explicitly. First, if you want to tell the compiler, "I meant to do hide-by-name" (the default), then you can use the Shadows keyword, as shown in this example:
Class Calculator Function Multiply(ByVal x As Integer, _ ByVal y As Integer) As Integer End Function Function Multiply(ByVal x As Long, _ ByVal y As Long) As Long End FunctionEnd Class Class CalculatorEx Inherits Calculator Shadows Function Multiply(ByVal x As Integer, _ ByVal y As Integer, _ ByVal z As Integer) _ As Integer End FunctionEnd Class
With the Shadows keyword, VB uses hide-by-name semantics and suppresses the compiler warning. If you would rather have hide-by-signature semantics like C#, use the Overloads keyword. Here is the same example using the Overloads keyword:
Class Calculator Function Multiply(ByVal x As Integer, _ ByVal y As Integer) As Integer End Function Function Multiply(ByVal x As Long, _ ByVal y As Long) As Long End FunctionEnd Class Class CalculatorEx Inherits Calculator Overloads Function Multiply(ByVal x As Integer, _ ByVal y As Integer, _ ByVal z As Integer) As Integer End FunctionEnd Class
In this scenario, VB uses hide-by-signature semantics. Just like in C#, if the compiler can combine the definitions of the methods, it will. In this example, the programmer will see CalculatorEx as having three versions of Multiply. Using the Overloads keyword also suppresses the compiler warning.
Both C# and VB.NET have a mechanism by which you can mark a function as virtual in the base class, and then override the method in the derived class. If you create an instance of the derived class when you override a method, even code in the base class that calls the method invokes the overridden version of the method instead of the original. In C#, marking a method as virtual is done with the virtual keyword, as follows:
class Account{ public virtual void MakeDeposit(int Amount) { }} class Checking : Account{ public override void MakeDeposit(int Amount) { }}
This example also shows how to override the method in the derived class by using the override keyword. To illustrate what it means to override a method, consider the following code:
Account acct = new Checking( );acct.MakeDeposit(500);
In this example, a variable of type Account is used to reference an object of type Checking. Because the object is of type Checking, and because Checking overrides Account's version of MakeDeposit, the runtime invokes Checking's version of MakeDeposit even if a variable of type Account is used.
VB.NET uses Overridable to mark a method as virtual and Overrides to override the method:
Class Account Overridable Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class Class Checking : Inherits Account Overrides Sub MakeDeposit(ByVal amount _ As Integer) End SubEnd Class
As in the C# example, if a programmer assigns a variable of type Account to an object of type Checking and invokes the MakeDeposit method, the runtime invokes the Checking version of MakeDeposit.
Overriding in VB is a little more complicated when the base class has overloaded methods, and it either marks all of them or only some of them as Overridable. Consider the following example:
Class Account Overridable Sub MakeDeposit( _ ByVal amount As Integer) End Sub Sub MakeDeposit(ByVal amount As Long) End SubEnd Class Class Checking : Inherits Account Overrides Sub MakeDeposit(ByVal amount As Integer) End SubEnd Class
In this case, Overrides also uses the hide-by-name semantics by default. (For a full explanation of hide-by-name, see the previous section, Section 1.3.7.) Essentially this means that someone using a variable of type Checking would only be able to see the MakeDeposit method in the Checking class and not the combination of all methods from both classes. The compiler gives you a warning when this happens. You cannot suppress the warning and use hide-by-name semantics by combining the Overrides keyword with the Shadows keyword. It is illegal to combine the keywords. However, you can choose hide-by-signature semantics instead by using the Overloads keyword together with Overrides in this fashion:
Class Checking : Inherits Account Overloads Overrides Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class
If a method is marked as virtual and you override the method in a derived class, the method continues to be virtual. This means that another developer can inherit from your derived class and override the method again. You can prevent the method from being further overridden by sealing it. Sealing in C# is done with the sealed keyword. Here is an example:
class Account{ public virtual void MakeDeposit(int Amount) { }} class Checking : Account{ public override sealed void MakeDeposit(int Amount) { }} class SuperChecking : Checking{ //*** this is illegal *** public override void MakeDeposit(int Amount) { }}
In this code, SuperChecking cannot override the MakeDeposit method further because the author of Checking marked it as sealed.
To seal a method in VB.NET you use the NotOverridable keyword:
Class Account Overridable Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class Class Checking : Inherits Account NotOverridable Overrides Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class Class SuperChecking Inherits Checking `***this is illegal*** Overrides Sub MakeDeposit( ByVal amount As Integer) End SubEnd Class
As in the C# example, SuperChecking is prevented from overriding the method further because the author of Checking marked the method as NotOverridable.
When you override a method, you may still want to use the code from the original method. You can invoke the original method in C# using the base keyword:
class Account{ public virtual void MakeDeposit(int Amount) { }} class Checking : Account{ public override void MakeDeposit(int Amount) { //do something before base.MakeDeposit(Amount); //do something after }}
The Checking class overrides the MakeDeposit method, but also invokes the original method in Account using the base keyword.
VB.NET also has a way of invoking the default method in the base using the MyBase keyword:
Class Account Overridable Sub MakeDeposit( ByVal amount As Integer) End SubEnd Class Class Checking Inherits Account Overrides Sub MakeDeposit(ByVal amount As Integer) 'do something before MyBase.MakeDeposit(amount) 'do something after End SubEnd Class
VB.NET has a feature for overriding methods that C# does not have. When a method is overridden, if you create an instance of the derived class, even code in the base class invokes the overridden method. However, in VB you can write code that invokes the original method using the MyClass keyword. First let's show what happens if you do not use MyClass:
Class Account Sub New( ) MakeDeposit(100) End Sub Overridable Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class Class Checking Inherits Account Overrides Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class
In this code, if you create an instance of the Checking class, the runtime invokes the constructor for Checking first. The code does not show a constructor for Checking, so the compiler adds a default constructor, which calls the constructor in the base class. As you can see, the Account class has a constructor. The code for the constructor calls the MakeDeposit method. However, because the method is overridden, the constructor in Account ends up executing MakeDeposit in Checking, not the original version in Account. This is the default behavior in both VB.NET and C#. However, VB.NET enables you to invoke the original method using the MyClass keyword as follows:
Class Account Sub New( ) MyClass.MakeDeposit(100) End Sub Overridable Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class
In this case, the code in the constructor for Account calls the Account version of MakeDeposit. Note that MyClass is not the same as Me in VB. Using Me would still call the derived version instead of the original version.
In C# you can mark a class as sealed. A sealed class is a class that cannot be used as a base class for another class—in other words, you cannot inherit from it. Here is an example of adding the word "sealed" to the class definition:
class Account{}sealed class Checking : Account{}//***this is illegal***class SuperChecking : Checking{}
In this code example, Account is not sealed, so Checking can inherit from it; however, Checking is a sealed class, so it is illegal for SuperChecking to inherit from it. This is not the place to have a full discussion of why you would seal, but normally you would seal a class to prevent someone from writing a subclass that overrides how you implemented an interface. Take for example the System.String class that Microsoft provides. It implements a series of critical interfaces: ICloneable, IComparable, etc. The runtime expects these interfaces to be implemented in a particular way. It would cause problems if you were to write your own string class and override the way those interfaces are implemented, and then pass instances of your string class to functions in the runtime that expected a System.String object (which you could do if you inherited from the class).
The equivalent of sealed in VB.NET is NotInheritable. Here is an example of applying this keyword to a class declaration:
Class AccountEnd Class NotInheritable Class Checking Inherits AccountEnd Class '***this is illegal ***Class SuperChecking Inherits CheckingEnd Class
In this code example, Checking is free to inherit from Account; however, SuperChecking cannot inherit from Checking because it is marked as NotInheritable.
How about the opposite, a class that you must inherit from? This is done when you write a class that is not meant to be instantiated directly. For example, throughout the book I have been using Account and Checking. It could be that I decide to write Account as a base class for Checking, Savings, etc. but that my intention was for developers not to create Account directly. This is done in C# with the abstract keyword, in the following fashion:
abstract class Account{}class Checking : Account{}
Class Account is marked as abstract, which means that you cannot create instances of it directly. You can inherit from it, and then create instances of the subclass. The abstract attribute is required when one of the methods in the class is an abstract method. An abstract method is a method you must override in the derived class. Here is an example of an abstract class with an abstract method:
abstract class Account{ public void ReportInfo( ) { System.Console.WriteLine( "Account is of type " + AccountType); } public abstract string AccountType { get;}} class Checking : Account{ public override string AccountType { get { return "Checking"; } }}
In this code example, AccountType is an abstract read-only property. That means that you must override the function and implement it in a derived class. Account provides other functionality, such as the ReportInfo function which uses the AccountType property, assuming that it has been implemented elsewhere.
The equivalent of abstract in VB.NET is MustInherit. Here is an example of a class using the MustInherit keyword:
MustInherit Class AccountEnd Class Class Checking Inherits AccountEnd Class
The Account class is marked as MustInherit, which means that it is not a creatable class. You must write a class that derives from it if you want to use the functionality it provides. Normally, as in C#, this keyword is used when a member of the class is marked as MustOverride. In that case the compiler forces you to add the MustInherit keyword to the class declaration. Here is an example of the MustInherit keyword in combination with MustOverride:
MustInherit Class Account Sub ReportInfo( ) System.Console.WriteLine( _ "Account is of type" + AccountType) End Sub MustOverride ReadOnly Property AccountType( ) _ As StringEnd Class Class Checking Inherits Account Overrides ReadOnly Property AccountType( ) _ As String Get Return "Checking" End Get End PropertyEnd Class
The Account class defines a readonly property that every class author that inherits from it must override. The compiler forces you to mark that class as MustInherit.
Interfaces are defined in C# and in VB.NET with the interface/Interface keyword. Here is a C# example that declares two interfaces:
using System.Runtime.CompilerServices;public interface IAccount{ void MakeDeposit(int Amount); int Balance { get; } [IndexerName("Details")] int this[int x]{get; set;}} public interface ISaveToDisk{ void Save( ); void Save(string Path);}
As you can see, each declaration begins with the interface keyword followed by the interface name. All methods in the interface are implicitly public. Notice that the IAccount interface contains a definition for a method, a property, and an indexer. To declare properties in an interface, you write the property type, followed by the property name, followed by curly brackets. If the property allows read access, you write get; inside the curly brackets, and if the property allows write access, you write set; inside the curly brackets. Of course, you can combine get; and set;. You don't add a semicolon after the closing curly bracket.
Declaring an indexer inside an interface uses a similar format as the property. The main differences are that you use the keyword this for the property name, and that you have to add at least one input parameter inside square brackets. As with other indexer declarations, you can optionally use the IndexerName attribute to set the property name for the indexer; otherwise it defaults to Item. Refer to Section 1.2.21 for details on how to declare and use indexers.
The second declaration, ISaveToDisk, shows that you can do method overloading in interface definitions.
VB.NET has an Interface construct like C#. The VB.NET equivalent of the above definitions is:
Public Interface IAccount Sub MakeDeposit(ByVal Amount As Integer) ReadOnly Property Balance( ) As Integer Default Property Details(ByVal x As Integer) _ As Integer End Interface Public Interface ISaveToDisk Sub Save( ) Sub Save(ByVal Path As String)End Interface
VB uses Interface/End Interface instead of interface and curly brackets. The declaration of interface members is also different. The Property keyword is used to declare a property. By default a property has read/write access, but you can change that by adding the keywords ReadOnly (as you can see in the second member of IAccount) or WriteOnly in front of the declaration. Indexers in VB are nothing more than properties that accept parameters and are marked as default properties. In IAccount, Details is a read/write indexer. Because an indexer is a property, you can use the ReadOnly and WriteOnly keywords to restrict accessibility.
As in C# you can overload methods in interfaces, as demonstrated in the declaration of ISaveToDisk.
In both languages you can derive one interface from another. Say for example that after releasing the first version of banking.dll, you discover that IAccount needs an extra method, MakeWithdrawal. It is good programming practice to leave the IAccount interface alone, and declare a new interface, IAccount2, that derives from IAccount, as follows:
public interface IAccount2: IAccount{ void MakeWithdrawal( );}
Inheriting one interface from another is done by adding a colon at the end of the class name, followed by the name of the interface from which you wish to inherit. You can inherit from more than one interface—you just separate the interfaces with commas, as in the following:
public interface IAccount2: IAccount, ISaveToDisk{ void MakeWithdrawal( );}
Inheriting one interface from another can also be done in VB.NET using the Inherits keyword:
Interface IAccount2 Inherits IAccount Sub MakeWithdrawal( )End Interface
To inherit from multiple interfaces, you can either add two Inherits statements or list all the interfaces in a single Inherits statement, separated by commas. Here is an example of inheriting from multiple interfaces:
'you can put both interface names'in the same Inherits lineInterface IAccount2 Inherits IAccount, ISaveToDisk Sub MakeWithdrawal( )End Interface 'or write a different Inherits line for'each interfaceInterface IAccount2 Inherits IAccount Inherits ISaveToDisk Sub MakeWithdrawal( )End Interface
Let's talk about how to implement interfaces in each language. In C# you first tell the compiler that your class intends to implement the interface. This is done in the same way that you specify class-based inheritance: using the colon followed by the interface name:
class Checking : IAccount
You can derive a class from multiple interfaces, but remember that you can only inherit from one concrete class. In the inheritance line you would list the non-interface class first, then the interfaces separated by commas.
The next step is to implement the methods. The simplest way to do this in C# is to add a public method with the same name as the interface's method and with the same signature. Here is an example:
interface IAccount{ void MakeDeposit(int Amount);} class AccountImpl : IAccount{ public void MakeDeposit(int Amount) { }}
As you can see from the example, AccountImpl inherits from the interface. It has a public MakeDeposit method with a name that matches the interface's method name and has a matching signature (the parameters are of the same type). When the compiler sees a public method with the same name and signature as the interface method, it uses the method as the implementation of the interface method. Suppose there were two interfaces with the same method name as in the following:
interface ISaveToDisk
{void Save( );
}
interface IWhales
{void Save( );
}
class Checking : ISaveToDisk, IWhales
{ public void Save( ) { }}
In this example, both ISaveToDisk and IWhales have a Save method with the same signature. If I derive my class from both interfaces and add a Save method with the same signature to my class, then the Save method becomes the implementation method for both Save interface methods. This could be good or bad; good because it saves on typing, bad because it assumes that I want the same implementation for both methods. This syntax has another side effect. For this syntax to work, the method must be marked public—anything other than public results in a compiler error saying that the interface method has not been implemented. Making the method public means that there are two ways to reach that method. One is through a variable of the implementing class' type, and one is through a variable of the interface type. Here is an example:
interface IAccount{ void MakeDeposit(int Amount);} class Checking : IAccount{ public void MakeDeposit(int Amount) { }} class App{ static void Main(string[] args) { Checking check = new Checking( ); check.MakeDeposit(500); IAccount acct = new Checking( ); acct.MakeDeposit(500); }}
As you can see in Main, you can call the MakeDeposit method either through a variable of type Checking or through a variable of type IAccount. However, this has the side effect that the implementation method is accessible outside of the assembly. You can block access to the method through the variable of the class' type and you can map two interface methods with the same name to two different implementations with another syntax C# provides for implementing methods. This other syntax involves making the implementation method's name equal to the name of the interface, plus a dot, plus the name of the interface method. Here is the example that defines two interfaces with the same name revisited:
interface IAccount{ void MakeDeposit(int Amount);} interface ISaveToDisk{ void Save( );} interface IWhales{ void Save( );} class Checking : ISaveToDisk,IWhales{ void ISaveToDisk.Save( ) { } void IWhales.Save( ) { }}
With this syntax, the implementation methods are private. The only way to reach them is through the interface. And you are not allowed to put an access modifier keyword such as public (or even private) in one of these methods. To invoke the methods in this fashion you would do the following:
ISaveToDisk sav = new Checking( );sav.Save( );
You can always reach the method through the interface even if the method is private in scope.
VB.NET uses a different approach for implementing interface methods. First of all, to tell the compiler that you are going to support an interface, you use the Implements keyword as follows:
Class AccountImpl Implements IAccountEnd Class
You can list more than one interface in the same Implements statement by separating each interface name with a comma. Or, you can add multiple Implements statements. Just like in C#, you can inherit from at most one non-interface class, but you can implement more than one interface. If you do inherit from a non-interface class, you must put the Inherits statement before any Implements statements.
To implement an interface method in VB, you add a method with any name; it does not have to be the same name as the interface method name. However, you must match the signature to the interface method (the number of parameters, and types of parameters). Then you write Implements and the method you are implementing at the end of the method declaration, as illustrated in the next example:
Class AccountImpl Implements IAccount Sub MakeDeposit(ByVal Amount As Integer) _ Implements IAccount.MakeDeposit End SubEnd Class 'or you can also use a different method name'as long as you match the method signature Class AccountImpl Implements IAccount Sub MyMethod(ByVal Amount As Integer) _ Implements IAccount.MakeDeposit End SubEnd Class
The important thing is to match the signature of the method and add the Implements section at the end of the method declaration.
All methods in VB are public by default. In the previous example, if you examine the second AccountImpl class, you'll see that there are two ways to invoke the implementation method. One is through a variable of type AccountImpl and one is through a variable of type IAccount, as shown below:
Interface IAccount Sub MakeDeposit(ByVal Amount As Integer)End Interface Class AccountImpl Implements IAccount Sub MyMethod(ByVal Amount As Integer) _ Implements IAccount.MakeDeposit End SubEnd Class Module Module1 Sub Main( ) Dim acct As New AccountImpl( ) acct.MyMethod(500) Dim iacct As IAccount = New AccountImpl( ) iacct.MakeDeposit(500) End SubEnd Module
Notice that you can reach the method through a variable of the class type. However, when invoking the method, you use the name you gave the implementation method. In our example the name was MyMethod. On the other hand, if you use the interface, then you refer to the method by the interface's method name (MakeDeposit in our example).
You can restrict how developers access this method through variables of the class type by adding an access modifier to the implementation method. For example, you could mark the method private, as in the following example:
Class AccountImpl Implements IAccount Private Sub MyMethod(ByVal Amount As Integer) _Implements IAccount.MakeDeposit
End SubEnd Class
With the above definition there is only one way to reach the method now: through an interface variable.
The VB syntax also enables you to map two interface methods to the same implementation. Consider the following example:
Interface ISaveToDisk
Sub Save( )
End Interface
Interface ISaveToNetwork Sub Save( )End Interface Class Checking Implements ISaveToDisk, ISaveToNetwork Sub SaveToAnything( ) _ Implements ISaveToDisk.Save, _ ISaveToNetwork.Save End SubEnd Class
As the code shows, you can list more than one interface method in the Implements portion of the declaration.
Suppose a class implements an interface. For the sake of argument, let's say class AccountImpl implements the IAccount interface. Then suppose that class Checking inherits from AccountImpl. By inheriting from AccountImpl, Checking automatically gains support for the AccountImpl interface. That is a good thing, but suppose that Checking would like to have a different implementation for one of the methods in AccountImpl. Both languages enable you to override an interface implementation from a base class; however, this is done differently in each language.
In C#, if you use the method of adding public functions to implement the interface, you can override by marking the implementation method as virtual:
interface IAccount{ void MakeDeposit(int Amount);} class AccountImpl : IAccount{ public virtual void MakeDeposit(int Amount) { }} class Checking : AccountImpl{ public override void MakeDeposit(int Amount) { }}
The code example above defines an interface called IAccount. It then implements the interface in AccountImpl. Checking derives from AccountImpl, so it gains full support for the interface. However, if Checking decided that it needs different functionality for MakeDeposit, one way to override the method is for the author of AccountImpl to mark the method as virtual. Then the developer of the Checking class can override the implementation.
Suppose that the author of the base class does not want to mark the method as virtual. Then, as the author of the derived class, you can reimplement the interface. This is done by adding the interface again to the inheritance list, as follows:
class AccountImpl : IAccount{ public void MakeDeposit(int Amount) { }} class Checking : AccountImpl, IAccount{ public void MakeDeposit(int Amount) { }}
It will not work correctly if you do not reimplement the interface; calling the method through the interface will call the base class's implementation. Note also that when you reimplement the interface, you do not have to reimplement every method in the interface.
This technique of reimplementing the interface should also be used if you implement the interface in the base class using private methods. The following example illustrates this:
class AccountImpl : IAccount{ void IAccount.MakeDeposit(int Amount) { }} class Checking : AccountImpl, IAccount{ void IAccount.MakeDeposit(int Amount) { }}
In VB you cannot reimplement an interface in the derived type. This means that the only way to override the implementation of an interface in a derived class is to mark the implementation method in the base class as Overridable and then override the method in the derived type. The following example illustrates how to do that:
Interface IAccount Sub MakeDeposit(ByVal Amount As Integer)End Interface Class AccountImpl Implements IAccount Public Overridable Sub MakeDeposit( _ ByVal Amount As Integer) _ Implements IAccount.MakeDeposit End SubEnd Class Class Checking Inherits AccountImpl Public Overrides Sub MakeDeposit( _ ByVal amount As Integer) End SubEnd Class
This technique makes it possible to have a different implementation for MakeDeposit in the Checking class. If you were to define a parameter of type IAccount, set it equal to an object of type Checking, and then call the MakeDeposit method, the runtime would execute the Checking version of MakeDeposit. The only problem with this approach is that it requires a developer to mark the base class method as Overridable.
A big part of interface-based programming is finding out whether a particular object supports a certain interface. In C# this is done in two ways. One way is to use the as operator. The as operator performs a safe cast. If the class does not implement the interface then the as operator returns null; otherwise it returns a reference to the interface you requested. This example uses the as operator:
static void SaveAccount(IAccount acct){ISaveToDisk sav = acct as ISaveToDisk;
if (sav != null) sav.Save( );}
This function receives an object that supports the IAccount interface. The code then asks the object if it supports the ISaveToDisk interface using the as operator. If it does, it returns a reference to the interface. If it doesn't, it returns null.
You can also find out in C# if an object supports a particular interface with the is operator. The is operator returns true if the object supports the interface and false if it does not. This example uses the is operator:
static void SaveAccount(IAccount acct){ if (acct is ISaveToDisk) { ISaveToDisk sav = (ISaveToDisk) acct; sav.Save( ); }}
In this example, the code finds out first if the object acct supports the ISaveToDisk interface. If it does, the is test returns true. Then, the code performs an explicit cast to the ISaveToDisk interface, which is done by putting the type you want to convert to in parentheses in front of the variable.
Incidentally, if we were to cast to an interface that the object does not support, the runtime would return an InvalidCastException exception. Here is an example of how to catch that exception:
static void SaveAccount(IAccount acct){try
{ISaveToDisk sav = (ISaveToDisk) acct;
} catch (System.InvalidCastException ex) { }}
Another way to test whether an object supports the interface is to attempt to cast the object to the particular interface and trap for the exception. This method is not very efficient because exceptions are performance expensive and should be avoided if possible.
In VB.NET you test whether an object supports an interface using the TypeOf/Is keywords. Here is an example of testing for an interface in VB:
Sub SaveAccont(ByVal acct As IAccount) If TypeOf acct Is ISaveToDisk Then Dim sav As ISaveToDisk = CType(acct, ISaveToDisk) sav.Save( ) End IfEnd Sub
The code tests whether the particular object supports the ISaveToDisk interface first. If it does, TypeOf/Is returns true. Next, the code does an explicit cast to the interface by using the CType function. CType is required if Option Strict is turned on for the project. Otherwise, in VB you would be able to do an implicit cast, in the following fashion:
Option Strict OffDim sav As ISaveToDisk = acct Option Strict OnDim sav As ISaveToDisk = CType(acct, ISaveToDisk)