.NET for Visual FoxPro Developers

Chapter 5
Object Orientation in C# and Visual Basic .NET

With the release of Visual FoxPro 3.0, FoxPro became object-oriented. For those of you who have already made the leap to object-oriented programming, you know how difficult the transition can be. Fortunately, once you’ve learned OOP in VFP, it’s easy to learn the basics of OOP in .NET. This chapter shows how different object-oriented features are implemented in C# and Visual Basic .NET, while providing a comparison to Visual FoxPro’s object-orientation.

By its very nature, this chapter is one of the more controversial in this book. The two previous chapters were devoted to the basics of C# and Visual Basic .NET syntax, but this chapter moves into the meatier topic of object-orientation, providing a side-by-side comparison of the two languages. Many of you may use the information in these chapters to decide which .NET language to choose—and choice of language is often a religious issue!

As in the previous chapters, I’ll point out the differences in C# and VB .NET and offer some editorial comments on the approach I think is better. Although both languages compile down to the same intermediate language (MSIL), they implement .NET’s object-oriented features a little differently—and in some cases, one language has object-oriented features not available in the other language. Throughout this chapter, I’ll draw comparisons to Visual FoxPro’s object-oriented features so you can leverage your existing knowledge of OOP.

Where do you put your code?

Visual FoxPro allows you to place code in class methods, in functions or procedures stored in PRG files, or as procedural code stored in PRG files.

In contrast, all code in C# must be placed in classes. This is similar to other pure object-oriented programming languages, such as Java and SmallTalk that impose more discipline
on developers.

Visual Basic .NET takes an approach more like Visual FoxPro, in allowing you to place code and variables in either classes or modules. Similar to a class, a module is derived from the System.Object class and used to encapsulate items contained within it. The main differences between a class and a module are a class can be instantiated and implement interfaces, but a module cannot.

Another difference is in referencing members of a class, you must specify the name of the class followed by the member (for example, Customer.GetCreditLimit). In contrast, members declared in a module are like global variables and functions accessed from anywhere within your application. By default, members of a module are shared  (For details, see the “Instance and static (Shared) members” section later in this chapter). You can compare placing code in modules to creating global variables, functions, and procedures in a Visual FoxPro PRG.

Are modules a “good thing” or a “bad thing”? I think the answer to this is subjective. Personally, I tend to prefer a more “pure” object-oriented approach, because I believe it promotes better programming practices. However, you may prefer the convenience of being able to create standalone functions and procedures in a Visual Basic .NET module.

Classes

At the very heart of any object-oriented language are classes. I’ll begin by discussing the mechanics of where and how you define classes.

In Visual FoxPro, you define classes visually in VCX files or manually in PRG files. Defining classes in C# and VB .NET takes an approach somewhere in between. In these languages, you can place class definitions in source files (either .cs or .vb files), which are similar to Visual FoxPro’s PRG files. However, Visual Studio .NET lets you easily navigate to a particular class and a specific method in a source code file using the class and member combo boxes at the top of the code-editing window.

The class combo box (Figure 1) contains a list of all classes defined in a source code file. Selecting a class from this combo box takes you to the class definition in the source code.

Figure 1. The class combo box, at the top left of the code-editing window, contains a list of all classes defined in the source code file.

The member combo box (Figure 2) contains a list of all members defined for the currently selected class. Selecting a member from this combo box takes you to the member in the source code file.

Figure 2. The member combo box at the top right of the code-editing window contains a list of all members for the currently selected class.

As you manually page or arrow up/down in the source code file, the selected items in these boxes change to reflect your current location in the source code—a nice feature.

Defining classes

In Chapter 3, “Introduction to C#”, and Chapter 4, “Introduction to Visual Basic .NET”, you learned the basics of defining classes. In this chapter I’ll review some of that information and expand on it.

Here’s an example of the most simple class definition in C#:

public class SimpleClass

{

}

And in VB .NET:

Public Class SimpleClass

 

End Class

In both cases, this code defines a class named “SimpleClass”. The public keyword is an access modifier specifying the visibility of the class. If you don’t specify an access modifier, the visibility defaults to “internal” for C# and the equivalent “friend” for VB .NET. Table 1 lists the access modifiers that apply to classes.

Table 1. C# and VB .NET class access modifiers

C#
Access modifier

VB .NET
Access modifier

Description

public

public

Indicates no restrictions for accessing the class.

internal

friend

Indicates the class is only accessible from code within the same assembly where the class is defined.

 

Defining fields (member variables)

As discussed in Chapter 3, “Introduction to C#”, variables declared at the class level are known as fields, and as discussed in Chapter 4, “Introduction to Visual Basic .NET”, they are known as member variables in VB .NET. I don’t want to skip mentioning this here, but check out the corresponding chapter for details on each of these.

Defining properties

C# and Visual Basic .NET properties are similar to Visual FoxPro properties with associated access and assign methods. They are implemented as get and set accessor methods containing any code you want. Here is an example of a property used to retrieve and store the value of a private field (member variable in VB .NET).


In C#:

private int age = 99;

 

public int Age

{

  get

  {

       return this.age;

  }

  set

  {

       this.age = value;

       }

  }

And in Visual Basic .NET:

Private _age As Integer = 99

 

Public Property Age() As Integer

    Get

        Return _age

    End Get

    Set(ByVal Value As Integer)

        _age = Value

    End Set

End Property

In this code, the property has a get method used to retrieve the value from a private field, and a set method that stores a value to the private field. Notice the C# code sample takes advantage of C#’s case sensitivity, by naming the private field age and the property Age. Because VB .NET isn’t case sensitive, you need to give any associated member variable a different name than the property.

Text Box: ¥

It is not required for a property to have an associated field (member variable), although they often do. For example, rather than getting and setting the value of a field, code within a property might read and write data instead.

Notice in the C# sample code, the set method uses an intrinsic parameter named value. When client code saves a value to a property, it is stored in this intrinsic value variable. In contrast, although the Set method in the VB .NET sample code explicitly declares a variable named Value, you are allowed to change the name of this variable (although I can’t think of a compelling reason to do so).

Read-only and write-only properties

In C#, if you want to create a read-only property, all you have to do is leave out the set accessor method. For example:


private int height;

 

public int Height

{

  get

  {

       return this.height;

  }

}

You do the same thing in Visual Basic .NET, but you must also add the ReadOnly keyword to the property definition:

Private _height As Integer

 

Public ReadOnly Property Height() As Integer

    Get

        Return Me._height

    End Get

End Property

If you want to create a write-only property in C#, just leave out the get accessor method. You do the same thing in Visual Basic .NET, but you must also add the WriteOnly keyword to the property definition.

Defining methods

A class doesn’t do much without methods. Here’s an example of a simple method declaration
in C#:

public void SimpleMethod()

{

}

And in Visual Basic .NET:

Public Sub SimpleMethod()

 

End Sub

This code defines a method named “SimpleMethod” that returns nothing (void, in C#). Both C# and VB .NET require parentheses after the name of the method. As shown in the next section, these parentheses contain a list of parameters passed to the method. In this case, even though there are no parameters, you must still add the parentheses as a placeholder.

Here’s an example of a C# method that returns a boolean value:

public bool SimpleMethodBool()

{

  return true;

}

This is exactly the same as the previous C# method declaration, except I specified the return value is boolean rather than void, and I added a line of code that returns a boolean value. This is extremely consistent syntax.

Now here’s the same method in Visual Basic .NET:

Public Function SimpleMethodBool() As Boolean

  Return True

End Function

Notice the method is declared as a function rather than a subroutine (Sub) and has an additional As clause specifying the type of the return value. What’s going on here?

In VB .NET, to specify a method return nothing, you must declare it as a subroutine (Sub). If a method returns a value, you must declare it as a Function and use the As clause to specify the return value type.

In all the methods shown above, the public keyword is an access modifier specifying the class visibility. Table 2 lists all access modifiers for C# and VB .NET members.

Table 2. C# and VB .NET class member access modifiers

C#
Access modifier

VB .NET
Access modifier

Description

public

Public

No restrictions for accessing the member.

protected

Protected

The member is only accessible from within the class or from classes derived from the class.

internal

Friend

The member is only accessible from code within the assembly where the member is defined.

protected internal

Protected Friend

The member is only accessible from within the class or from code within the assembly where the member is defined.

private

Private

The member is only accessible within the containing class.

Specifying method parameters

There are a few different kinds of method parameters in C# and VB .NET (Table 3) described in the following sections.

Table 3. C# and VB .NET method parameters

C# parameter keywords

VB .NET
Access modifier

Description

(no keyword needed)

ByVal

Value parameter—the default for C# and VB .NET

reference

ByRef

Reference parameter

params

ParamArray

Parameter array

out

Not required

Output parameter

n/a

Optional

Optional parameter

 


Value parameters

Just as in Visual FoxPro, the default parameter type in both C# and Visual Basic .NET is a value parameter. In practical terms, if the method you call changes the value of the parameter, the value is not changed in the calling method. Here’s an example of a method with a value parameter in C#:

public void ValueParam(int age)

{

  age++;

}

And in Visual Basic .NET:

Public Sub ValueParam(ByVal age As Integer)

    age = age + 1

End Sub

In C#, if you don’t specify otherwise, the parameter is passed by value. In VB .NET, if you don’t specify the kind of parameter, Visual Studio .NET automatically inserts the ByVal keyword for you.

Reference parameters

When you pass a parameter by reference, if the method changes its value, the value is also changed in the calling method. Here’s an example using a reference parameter in C#:

public void ReferenceParam(ref int age)

{

  age++;

}

And in Visual Basic .NET:

Public Sub ReferenceParam(ByRef age As Integer)

    age += 1

End Sub

In C#, if a method accepts a reference parameter, you must specify in the method call that you are passing the value by reference:

PtDemo.ReferenceParam(ref age);

In Visual Basic .NET, you don’t have to specify you’re passing the value by reference:

PtDemo.ReferenceParam(Age)


Array parameters

You can pass a variable number of parameters to a C# or VB .NET method by declaring an array parameter. Within the method body, you can use a foreach loop to access all items in the passed array.

In C#:

public void ArrayParam(params int[] ages)

{

  string AgeMsg = "";

 

  foreach (int i in ages)

  {

       AgeMsg += i + ": ";

  }

 

  MessageBox.Show("Ages: " + AgeMsg, "Array Parameter demo");

}

In Visual Basic .NET:

Public Sub ArrayParam(ByVal ParamArray ages() As Integer)

    Dim AgeMsg As String = ""

 

    Dim i As Integer

    For Each i In ages

        AgeMsg += i.ToString() & ": "

    Next i

 

    MessageBox.Show("Ages: " & AgeMsg, "Array Parameter demo")

End Sub 'ArrayParam

Output parameters

C# has output parameters—a parameter type not available in Visual Basic .NET. An output parameter is similar to a reference parameter, but it allows you to pass a variable to a method without first specifying an initial value. If you remember from Chapter 3, “Introduction to C#”, in an effort to encourage good programming practices, C# requires you to specify a value for all variables before using them. Output parameters are the one exception to the rule. To demonstrate, here’s a C# method that declares a single output parameter:

public void OutParam(out int age)

{

  age = 39;

}

And here’s an example of code calling this method:

int JackBennysAge;

PtDemo.OutParam(out JackBennysAge);

Notice this code declares the variable JackBennysAge, but never stores an initial value. So why aren’t output parameters available in VB .NET? Because VB .NET does not require you to initialize variables (although it’s a good idea to specify initial values anyway!).

Optional parameters

Visual Basic .NET has a type of parameter not available in C#—optional parameters. Optional parameters provide a way to specify a particular parameter that is not mandatory. When declaring an optional parameter, you must specify a default value in case the caller doesn’t pass a value for the optional parameter. For example, here’s a VB .NET method that declares a single optional parameter with a default value of 50:

Public Sub OptionalParam(Optional ByVal age As Integer = 50)

At first, optional parameters might seem like a great feature. However, you should probably avoid using them! One reason is C# does not recognize optional parameters—it sees them as mandatory, just like every other parameter. This is an important consideration if you anticipate C# clients may be accessing your code. Optional parameters can also introduce problems if you ever decide to change the default value of the optional parameter. For details, see the MSDN article “Exploiting New Language Features of Visual Basic .NET, Part 2” (http://msdn.microsoft.com/msdnmag/issues/01/08/instincts/instincts0108.asp).

In reality, avoiding optional parameters is not a real loss. You can actually accomplish the same thing by means of overloaded methods.

Overloaded methods

In Visual FoxPro, all class methods must have unique names. You may be surprised to learn this is not true in C# or Visual Basic .NET. Both languages allow you to create multiple methods with the same name—as long as the number and type of parameters is different. For example, the following code defines a class with two methods, both named “DisplayMessage”.

In C#:

  public class OverloadedMethodsDemo

  {

       public void DisplayMessage(string message, string caption)

       {

          MessageBox.Show(message, caption);

       }

 

       public void DisplayMessage(string message)

       {

          this.DisplayMessage(message, "Application Message");

       }

  }

And in Visual Basic .NET:

Public Class OverloadedMethodsDemo

 

    Public Overloads Sub DisplayMessage(ByVal message As String, _

        ByVal caption As String)

        MessageBox.Show(message, caption)

    End Sub 'DisplayMessage

 

    Public Overloads Sub DisplayMessage(ByVal message As String)

        Me.DisplayMessage(message, "Application Message")

    End Sub 'DisplayMessage

 

End Class 'OverloadedMethodsDemo

Although these methods have the same name, they have a different signature. In object-oriented terminology, the word signature refers to the type and order of a method’s parameters. In this case, the first overload of the DisplayMessage method accepts two string parameters—one named “message” and the other named “caption”. The second overload accepts only one string parameter named “message”.

When a client calls the DisplayMessage method, the compiler examines the parameters passed to the method and determines which overloaded method should be called. For example, the following code instantiates the OverloadedMethodsDemo class. The second line of code calls the DisplayMessage method with a single string parameter, and the third line of code calls the method with two string parameters.

In C#:

OverloadedMethodsDemo OverloadDemo = new OverloadedMethodsDemo();

OverloadDemo.DisplayMessage("Overloaded methods are great!");

OverloadDemo.DisplayMessage("Overloaded methods are great!", "Overload demo");

In Visual Basic .NET:

Dim OverloadDemo As New OverloadedMethodsDemo()

OverloadDemo.DisplayMessage("Overloaded methods are great!")

OverloadDemo.DisplayMessage("Overloaded methods are great!", "Overload demo")

The compiler is satisfied with these two calls because there are overloaded methods matching each set of parameters. However, if you try to call the DisplayMessage method with an incorrect number of parameters (for example, no parameters, more than two parameters, or parameters that are not string types), the compiler displays an error message.

Visual Studio .NET’s IntelliSense lets you know if a method you are calling is overloaded. For example, Figure 3 shows the popup displayed by VS .NET when entering code that calls the DisplayMessage overloaded method.

Figure 3. When you enter code that calls an overloaded method in Visual Studio .NET’s code editor, a popup displays showing details of the overloaded methods.

So, where would you use overloaded methods? When you have a method with optional parameters, you should create overloaded methods representing the different variations of parameters that can be passed to the method. This is much cleaner than having a single method checking which parameters have been passed.

Constructor methods

All Visual FoxPro classes possess an Init method that automatically executes when a class is instantiated. C# and VB .NET also have constructor methods that perform a similar function.

In C#, you declare a constructor method by adding a method with the same name as the containing class:

public class ConstructorDemo

{

  public ConstructorDemo()

  {

  }

}

In Visual Basic .NET, you declare a constructor by adding a New method to the class definition:

Public Class ConstructorDemo

    Public Sub New()

 

    End Sub

End Class

I actually prefer the VB .NET convention, because it’s easier to quickly identify a constructor that’s consistently named the same.

Notice in both code samples that neither constructor specifies a return value. This is because .NET constructors are not allowed to return values unlike Visual FoxPro where you can return a boolean False to prevent a class from instantiating.

Constructors with parameters

As in Visual FoxPro, you can specify parameters for constructor methods in .NET. This allows you to pass values to a class when it is first instantiated.

Here’s an example in C#:

public class ConstructorDemo

{

  private string connectString;

 

  public ConstructorDemo(string connect)

  {

       this.connectString = connect;

  }

}

And in Visual Basic .NET:

Public Class ConstructorDemo

    Private connectString As String

 

    Public Sub New(ByVal connect As String)

        Me.connectString = connect

    End Sub

 

End Class

In this code, the value passed in the constructor is used to initialize the value of a private field. If you don’t explicitly declare a constructor, a default constructor is automatically provided.

Here is an example of passing a value to the constructor of a class when it is instantiated.

In C#:

ConstructorDemo ConstructDemo = new

  ConstructorDemo("server=(local);uid=sa;pwd=;database=Northwind;");

And in Visual Basic .NET

Dim ConstDemo As _

    New ConstructorDemo("server=(local);uid=;pwd=;database=NorthWind;")

        As with other methods, you can also create overloaded constructor methods. Based on the parameters you pass when instantiating a class, the appropriate constructor is called.

Destructor methods

Visual FoxPro classes have a Destroy method that executes when an object is released. Typically you place code in this method to perform cleanup for the object. This works well because you have complete control over when an object is destroyed in Visual FoxPro.

In contrast, .NET has something called non-deterministic finalization. This means you don’t have explicit control over when an object is destroyed. When the last reference to a .NET object is released, the object itself is not released from memory. The object is not released until the next time the common language runtime’s garbage collector runs (see the “Garbage collection” section later in this chapter for details).

In both C# and Visual Basic .NET classes, you can add a Finalize method that executes when the object is destroyed. Just remember you can’t determine when this method is executed.

Class inheritance

One key feature of object-orientation is class inheritance, also known as implementation inheritance. C# and Visual Basic .NET both have single inheritance, meaning a class can only have one parent class. In .NET, the term base class is used in place of Visual FoxPro’s “parent class”. Personally, I prefer the term “parent class” because it’s more descriptive and more readily understood.

Specifying a base class

When you define a class in C# or VB .NET, if you don’t specify otherwise, its default base class is the .NET Framework’s System.Object class. Here is an example showing how to specify a base class.

In C#:

public class BaseClassDemo : Component

{

}

In Visual Basic .NET:

Public Class BaseClassDemo

    Inherits Component

End Class

 This code defines a class named BaseClassDemo derived from the .NET Framework’s Component class (System.ComponentModel.Component). In C#, you declare a base class by placing a colon followed by the base class name (: Component) at the end of the first line of the class declaration. In Visual Basic .NET, you place the keyword Inherits followed by the base class name on the second line of the class declaration.

Inheritance works the same way in .NET as it does in Visual FoxPro. A subclass inherits all members (properties, events, methods, and fields) from its base class, including implementation code.

Overriding inherited methods

At times, you may want a class to override an inherited method. In Visual FoxPro, the simple act of placing code in a method causes the method to be overridden. Unfortunately, in VFP all it takes is a single space character to unintentionally override a parent method.

You can “accidentally” override a method in C# and Visual Basic .NET by creating a method in a class with the same name and signature as an inherited method. For example, the following code declares a class named MyBaseClass with a single method named DisplayMessage. It also declares a subclass of MyBaseClass named “OverrideMethodDemo” that contains a duplicate DisplayMessage method.

In C#:

public class MyBaseClass

{

  public void DisplayMessage()

  {

       MessageBox.Show("Base class method!", "Override demo");

  }

}

 

public class OverrideMethodDemo : MyBaseClass

{

  public void DisplayMessage()

  {

       MessageBox.Show("Subclass method!", "Override demo");

  }

}


In Visual Basic .NET:

Public Class MyBaseClass

    Public Sub DisplayMessage()

        MessageBox.Show("Base class method!", "Override Demo")

    End Sub

End Class

 

Public Class OverrideMethodDemo

    Public Sub DisplayMessage()

        MessageBox.Show("Subclass method!", "Override Demo")

    End Sub

End Class

When you run this code, the OverrideMethodDemo.DisplayMessage method is executed, but the base class method is not. In reality, this is not a legal way to override a method in either C# or Visual Basic .NET. In both languages, the compiler catches this error and displays it as a warning (Figure 4).

Figure 4. The C# and VB .NET compilers display a warning if you create a method in a class with the same name and signature as an inherited method.

The following code demonstrates the proper syntax for overriding methods by using the override keyword (C#) or the Overrides keyword (VB .NET) in the method declaration.

In C#:

  public class MyBaseClass

  {

       public virtual void DisplayMessage()

       {

          MessageBox.Show("Base class method!", "Override demo");

       }

  }

 

  public class OverrideMethodDemo : MyBaseClass

Text Box: 		public override void DisplayMessage()

  {

       {

          MessageBox.Show("Subclass method!", "Override demo");

       }

  }

And in Visual Basic .NET:

Public Class MyBaseClass

    Public Overridable Sub DisplayMessage()

        MessageBox.Show("Base class method!", "Override Demo")

    End Sub

End Class

 

Public Class OverrideMethodDemo

Text Box:     Public Overrides Sub DisplayMessage()

    Inherits MyBaseClass

        MessageBox.Show("Subclass method!", "Override Demo")

    End Sub

End Class

When you instantiate the OverrideMethodDemo class and run its DisplayMessage method, the code in the OverrideMethodDemo subclass is executed, but the DisplayMessage method in the parent is not executed.

Virtual (Overridable) methods

Virtual methods can be overridden by a subclass. In Visual FoxPro, all methods are virtual because you can override any inherited public or protected method simply by placing code in the method of a subclass. There is no such thing as a “non-virtual” method in Visual FoxPro.

In contrast, methods are non-virtual by default in C# and VB .NET and cannot be overridden. In order to override a method, it must be specifically marked as virtual (C#) or Overridable (VB .NET) in the base class. If you look closely at the code in the previous section, you will see the DisplayMessage method in MyBaseClass was marked virtual (Overridable for VB .NET).

Text Box: ¥

If you override a virtual method, the method in the subclass marked “override” is automatically virtual too.

Extending inherited methods

More often than not, you extend rather than completely override an inherited method. In Visual FoxPro you accomplish this by placing code in a method, then issuing a DODEFAULT command. You can run DODEFAULT first, before executing your subclass code, or you can run your code first, and then issue a DODEFAULT.

To call a base class method in C#, you use the base keyword:

public class CallBaseMethodDemo : MyBaseClass

{

  public override void DisplayMessage()

  {

       MessageBox.Show("Subclass method!", "Call base method demo");

       base.DisplayMessage();

  }

}

To call a base class method in VB .NET, you use the MyBase keyword:

Public Class CallBaseMethodDemo

    Inherits MyBaseClass

    Public Overrides Sub DisplayMessage()

        MessageBox.Show("Subclass method!", "Call base method demo")

        MyBase.DisplayMessage()

    End Sub

End Class

In this example, code first executes in the subclass method and afterwards calls the base class method. You can easily reverse this order by placing the call to the base class first in the subclass method.

Polymorphism and virtual methods

Visual FoxPro is a weakly typed language, so you don’t specify the types of variables. You simply declare a variable and instantiate an object. For example:

x = CREATEOBJECT("MyClass")

As you’ve already seen, when instantiating an object in C# and in Visual Basic .NET,
you always declare the type of the variable that holds a reference to the object (assuming
VB .NET’s Option Strict is “On”). For example, the following code declares a variable
named “ClientObj” of the type Client, and then stores a new instance of the Client class into the variable.

In C#:

Client ClientObj;

ClientObj = new Client();

And in Visual Basic .NET:

Dim Client As ClientObj

ClientObj = New Client()

In this example, the code declares a variable of a specific type, and then instantiates an object from that type—no surprises here.

However, in both C# and VB .NET, when you declare a variable of a particular type it can also hold a reference to any subclass of that type. Take for example the class hierarchy shown in Figure 5, which shows Client and Invoice classes derived from the ABusinessObject class.

Figure 5. When you declare a variable of a particular type it can also hold a reference to any subclass of that type.

Given this hierarchy, you can declare a variable of type ABusinessObject and then store a reference to either the Client or Invoice object in this variable.

In C#:

ABusinessObject BizObj;

BizObj = new Client();

BizObj = new Invoice();

And in Visual Basic .NET:

Dim BizObj As ABusinessObject

BizObj = New Client()

BizObj = New Invoice()

This technique allows you to write more generic code that works with families of objects, making use of object-oriented polymorphism rather than coding to a specific class.

Hiding inherited methods

So far, you’ve learned about overriding and extending inherited methods. However, there are other situations where you may want to completely hide an inherited method and redeclare it.

To hide an inherited method in C#, use the new keyword in the method declaration:

public class HideMethodDemo : MyBaseClass

{

  public new void DisplayMessage()

  {

       MessageBox.Show("Subclass method!", "Hide method demo");

  }

}

To hide an inherited member in Visual Basic .NET, use the shadows keyword in the method declaration:

Public Class HideMethodDemo

    Inherits MyBaseClass

    Public Shadows Sub DisplayMessage()

        MessageBox.Show("Subclass method!", "Hide method demo")

        MyBase.DisplayMessage()

    End Sub

End Class

In what situations might you choose to completely hide an inherited method? First of all, you can use it to override a method not marked as virtual (or Overridable). Although I said earlier only virtual methods can be overridden, you can get around this rule by redeclaring a method with the new or shadows keyword.

 


Text Box: ¥Be judicious when deciding to hide an inherited method. If a base class method has not been marked as “virtual”, the developer may have a good reason for not allowing you to override the method. Test your code well!

For example, the following code declares a class named BaseClass with a single non-virtual method named “DisplayMessage”. It then declares a subclass of HideMethodBase named “HideMethodDemo” that redeclares the DisplayMessage method. This method even contains a call to the base class DisplayMessage method!

In C#:

public class HideMethodBase

{

  public void DisplayMessage()

  {

       MessageBox.Show("Base class method!", "Override demo");

  }

}

 

public class HideMethodDemo : HideMethodBase

Text Box: 	public new void DisplayMessage()

{

  {

       MessageBox.Show("My new method", "Hide method demo");

       base.DisplayMessage();

  }

}

In Visual Basic .NET:

Public Class HideMethodBase

    Public Sub DisplayMessage()

        MessageBox.Show("Base class method!", "Override demo")

    End Sub

End Class

 

Public Class HideMethodDemo

    Inherits HideMethodBase

Text Box:     Public Shadows Sub DisplayMessage()

    ' Hide the DisplayMessage method in the base class

        MessageBox.Show("My new method", "Hide method demo")

        MyBase.DisplayMessage()

    End Sub

End Class

There’s one “gotcha” when hiding an inherited method in this way. If you use the polymorphic trick of declaring a variable of the type HideMethodBase, but you actually instantiate an instance of the HideMethodDemo subclass instead, you get unexpected behavior when calling the DisplayMessage method.


Here’s this scenario in C#:

HideMethodBase HideBase = new HideMethodDemo();

HideBase.DisplayMessage();

And in Visual Basic .NET:

Dim HideBase As HideMethodBase = New HideMethodDemo()

HideBase.DisplayMessage()

When you run this code, rather than calling the DisplayMessage method belonging to the HideMethodDemo class, it calls the DisplayMessage method belonging to the HideBase base class instead! This is opposite of the behavior you might expect, so you need to write code with this in mind.

Another good example of a scenario where you can hide an inherited method involves third-party .NET components. Say you purchase a third-party component, subclass it, and add a custom method called PrintMessage. What happens if the company who created the component releases a new version and adds their own PrintMessage method? This leaves you with two choices. You can rename your custom method, but you may have countless lines of code in your applications calling the PrintMessage method and they all need to change.

Another option is to hide the newly inherited method causing the problem. You can then add a custom method to your subclass that calls it in the base class.

Here’s the solution shown in C#:

public class MyBaseClass

{

  public virtual void PrintMessage()

  {

       MessageBox.Show("Printing message!", "Hide method demo");

  }

}

 

public class HideMethodDemo : MyBaseClass

{

  // Hide the PrintMessage method in the base class

Text Box: 	public new void PrintMessage()

  {

       MessageBox.Show("Hiding the inherited method!", "Hide method demo");

  }

 

  // Create a custom method that calls the base class method

  public void PrintMsg()

Text Box: 		base.PrintMessage();

  {

  }

}


And in Visual Basic .NET:

Public Class MyBaseClass

    Public Overridable Sub PrintMessage()

        MessageBox.Show("Printing message!", "Hide method demo")

    End Sub

End Class

 

Public Class HideMethodDemo

    Inherits MyBaseClass

Text Box:     Public Shadows Sub PrintMessage()

    ' Hide the PrintMessage method in the base class

        MessageBox.Show("Hiding the inherited method!", "Hide method demo")

    End Sub

    ' Create a custom method that calls the base class method

Text Box:         MyBase.PrintMessage()

    Public Sub PrintMsg()

    End Sub

End Class

In this code, the HideMethodDemo class hides the PrintMessage method in the base class. It then declares a method named PrintMsg that calls PrintMessage method in the base class.

Preventing inheritance

As mentioned in the section “Virtual (Overridable) Methods”, C# and VB .NET methods are non-virtual by default, meaning they cannot be overridden in subclasses. In contrast, methods marked as “virtual” can be overridden.

There may be cases where you override a virtual method in a base class, but you don’t want other subclasses to override the method in your class. You can prevent someone from overriding your method by marking it as sealed (C#) or NotOverridable (VB .NET).

 For example, the following code declares a base class named “PreventInheritanceBase” containing a single virtual method named “DisplayMessage”. It also declares a subclass of PreventInheritanceBase named “PreventInheritanceSubclass” that marks the DisplayMessage method as sealed. This prevents subclasses of PreventInheritanceSubclass from further overriding this method.

In C#:

public class PreventInheritanceBase

{

  public virtual void DisplayMessage()

  {

       MessageBox.Show("This is a virtual method!", "Prevent inheritance demo");

  }

}

 


public class PreventInheritanceSubclass : PreventInheritanceBase

Text Box: 	public override sealed void DisplayMessage()

{

  {

       MessageBox.Show("Sealed method!", "Prevent inheritance demo");

  }

}

In Visual Basic .NET:

Public Class PreventInheritanceBase

 

    Public Overridable Sub DisplayMessage()

        MessageBox.Show("This is a virtual method!", _

            "Prevent inheritance demo")

    End Sub 'DisplayMessage

End Class 'PreventInheritanceBase

 

Public Class PreventInheritanceSubclass

    Inherits PreventInheritanceBase

Text Box:     Public NotOverridable Overrides Sub DisplayMessage()

        MessageBox.Show("This overrides a virtual method, then seals it!", _

            "Prevent inheritance demo")

    End Sub 'DisplayMessage

End Class 'PreventInheritanceSubclass

In addition to preventing inheritance at the method level, you can also specify an entire class cannot be inherited using the sealed keyword (C#) or the NotInheritable keyword (VB .NET). For example, the following code declares the “PreventClassInheritDemo” class cannot be subclassed.

In C#:

public sealed class PreventClassInheritDemo

{

}

In Visual Basic .NET:

Public NotInheritable Class PreventClassInheritDemo

 

End Class

Abstract classes and methods

There are two main types of classes in object-oriented programming—concrete and abstract. All of the classes you have seen so far are concrete; they have code in their methods that implements specific functionality. For example, in the previous section, the DisplayMessage method displays a message to the user. Concrete classes are meant to be instantiated and their methods called by client code.

In contrast, an abstract class is not intended to be instantiated. Its main purpose is to define an interface for a family of classes. It is abstract in the sense that it is conceptual as opposed to concrete. For example, you may conceive the need to create a class to access application settings. This class needs a method to retrieve settings and another method to
save settings.

The following code shows how to declare an abstract class named “AppSettingsBase” that represents this concept.

In C#:

public abstract class AppSettingsBase

{

  public abstract string GetSetting(string key);

  public abstract void SetSetting(string key, string value);

}

And in Visual Basic .NET:

Public MustInherit Class AppSettingsBase

 

    Public MustOverride Function GetSetting(ByVal key As String) As String

    Public MustOverride Sub SetSetting(ByVal key As String, _

        ByVal value As String)

End Class 'AppSettingsBase

 To mark a class as abstract, use the modifier abstract (C#) or MustInherit (VB .NET) in the class definition. In the AppSettingsBase class there are two abstract methods. These abstract methods represent the concept of retrieving and saving application settings. An abstract method does not contain implementation code. It simply defines a method signature including the method name, parameters and their types, and a return value and its type. To mark a method as abstract, use the modifier abstract (C#) or MustOverride (VB .NET).

You now have a class that defines an interface for retrieving and saving application settings. Now you can create concrete implementations of this abstract class to do the real work. For example, Figure 6 shows the abstract AppSettingsBase class with two subclasses. AppSettingsRegistry accesses settings stored in the Windows Registry and AppSettingsXml accesses settings stored in an XML file.

Text Box: ¥

UML syntax dictates abstract class names are shown in italics and concrete classes are shown in regular font. It also dictates abstract methods are shown in italics, but the tool I used to create the diagram (Rational Rose) doesn’t allow you to mark a method as abstract!

Figure 6. You can define an abstract class that defines an interface for a family of related classes.

The following code defines the AppSettingsRegistry class in C#:

public class AppSettingsRegistry : AppSettingsBase

{

  public override string GetSetting(string key)

  {

       string setting = "";

       // Code that reads a setting from the Windows Registry

       return setting;

  }

  public override void SetSetting(string key, string value)

  {

       // Code that stores a setting to the Windows Registry

  }

}

And in Visual Basic .NET:

Public Class AppSettingsRegistry

    Inherits AppSettingsBase

 

    Public Overrides Function GetSetting(ByVal key As String) As String

        Dim setting As String = ""

        ' Code that reads a setting from the Windows Registry

        Return setting

    End Function 'GetSetting

 

    Public Overrides Sub SetSetting(ByVal key As String, _

        ByVal value As String)

        ' Code that stores a setting to the Windows Registry

    End Sub 'SetSetting

End Class 'AppSettingsRegistry

 Obviously, these classes don’t do much “as is” because there are comments in the methods where there should be implementation code. Notice the subclass provides an implementation for both the GetSetting and SetSetting methods. When you create a class derived from an abstract class, both C# and VB .NET require you provide an implementation for all abstract methods. If you don’t, the compiler displays a warning accordingly.

Concrete methods in abstract classes

C# and VB .NET allow you to have both abstract and concrete methods in an abstract class. Why would you declare a concrete method in an abstract class? If there is common or default behavior you want all classes to inherit, you can create concrete methods containing code inherited by all subclasses. A subclass can choose to override a concrete method inherited from an abstract class if the default behavior does not suit that particular class.

When to create abstract classes

So, in what situations would you create an abstract base class? You should create an
abstract class when there are multiple ways to implement application functionality. The application settings class just discussed is a good example of this. There are multiple ways
you can implement an application settings class—by storing settings in the Windows
Registry, an XML file, an INI file, and so on. You can create a concrete class for each of
these implementations.

Instantiating concrete subclasses

To make the best use polymorphism with abstract and concrete classes, you should write generic code when working with concrete subclasses.

For example during application startup, you might instantiate a concrete AppSettingsRegistry class. For the most flexibility, you should declare a field of the type AppSettingsBase and store an instance of the concrete subclass in this field.

In C#:

public class MyApp

Text Box: 	public AppSettingsBase AppSettingsObj;

{

 

  public MyApp()

  {

       this.CreateAppSettingsObj();

  }

 

  public virtual void CreateAppSettingsObj()

Text Box: 		this.AppSettingsObj = new AppSettingsRegistry();

  {

  }

}

And in Visual Basic .NET:

Public Class MyApp

 

Text Box:     Public AppSettingsObj As AppSettingsBase

    Public Sub New()

        Me.CreateAppSettingsObj()

    End Sub 'New

 

    Public Sub CreateAppSettingsObj()

        Me.AppSettingsObj = New AppSettingsRegistry()

    End Sub 'CreateAppSettingsObj

End Class 'MyApp

 This code declares a class named “MyApp” with a field named “AppSettingsObj” of the type AppSettingsBase—the abstract class defined in the previous section. This field holds a reference to the application settings object. In the constructor of this class, a call is made to the CreateAppSettingsObj method containing code that creates an instance of AppSettingsRegistry and stores it in the AppSettingsObj field. This is the only method in your application that needs to know the specific concrete class that was instantiated. All other code in your application references the application settings object as if it were of the type AppSettingsBase.

Text Box: ¥

If you add new members to concrete subclasses, you can’t access those members when referencing an object by the abstract base class type. To keep your code as generic as possible, try to add methods at the abstract level rather than the concrete level.

The real beauty of this design is its extensibility. Imagine you later decide to save application settings to a DBF, rather than the Registry. All you have to do is create a new concrete subclass of AppSettingsBase (for example, AppSettingsDbf) to access settings stored in a DBF and change the code in the CreateAppSettingsObj to instantiate the new class.

In C#:

  public virtual void CreateAppSettingsObj()

  {

       this.AppSettingsObj = new AppSettingsDbf();

  }

And in Visual Basic .NET:

Public Overridable Sub CreateAppSettingsObj()

   Me.AppSettingsObj = New AppSettingsDbf()

End Sub 'CreateAppSettingsObj

This makes your application far more extensible, and maintainable.

Programming to an interface rather than an implementation

What you have just seen is an example of “programming to an interface rather than an implementation”. This concept is commonly discussed in object-oriented books, but I find many developers have difficulty understanding this concept.

“Programming to an implementation,” is undesirable. It means writing software hard-coded to a specific concrete functionality. For example, you might hard-code reading and writing application settings to an XML file.

In contrast, “programming to an interface” is desirable. In this particular case, it means creating an abstract base class to define an interface for a family of classes and writing code to talk to the interface.

Interface inheritance

When Visual FoxPro 3 was released, it introduced implementation inheritance to FoxPro. With implementation inheritance, subclasses inherit the properties, events, and methods of their base class, as well as any associated code. Visual FoxPro 7 introduced interface inheritance, (which was unfortunately limited to COM components). In contrast, C# and Visual Basic .NET have full support for both implementation and interface inheritance.

Interfaces are similar to classes in defining properties and methods that other classes can inherit. However, an interface only defines method signatures. It does not contain any implementation code that can be inherited. For example, the following code defines an interface named IDbConnection with four properties and six methods.

Text Box: ¥

All your interface names should be Pascal-cased and prefixed with an uppercase “I”. This means the first two letters of an interface name are always in upper case (for example, IDbConnection). This is a naming convention used by Microsoft that helps easily differentiate between classes and interfaces.

If you’re familiar with ADO.NET, the IDbConnection interface should look familiar to you. It is one of the interfaces defined in the .NET Framework’s System.Data namespace.

In C#:

public interface IDbConnection

{

  // Properties

  string ConnectionString();

  string ConnectionTimeOut();

  string Database();

  ConnectionState State();

 

  // Methods

  IDbTransaction BeginTransaction();

  IDbTransaction BeginTransaction(IsolationLevel level);

  void ChangeDatabase(string databaseName);

  void Close();

  IDbCommand CreateCommand();

  void Open();

}

In Visual Basic .NET:

Public Interface IDbConnection

   ' Properties

   Function ConnectionString() As String

   Function ConnectionTimeOut() As String

   Function Database() As String

   Function State() As ConnectionState

  

   ' Methods

   Overloads Function BeginTransaction() As IDbTransaction

   Overloads Function BeginTransaction(level As IsolationLevel) _

       As IDbTransaction

   Sub ChangeDatabase(databaseName As String)

   Sub Close()

   Function CreateCommand() As IDbCommand

   Sub Open()

End Interface 'IDbConnection

As you can see, defining an interface is similar to defining a class, except there is no code in the properties or methods.

Implementing interfaces

After you have defined an interface, you can specify that one or more classes implement the interface. When a class implements an interface, it is agreeing to a contract. This contract specifies the class will implement all the properties and methods in the interface. For example, there are a few classes in the .NET Framework class library that implement the IDbConnection interface including SqlConnection, OracleConnection, and OleDbConnection. If you look at the help file for these classes, you see each of them implement all the properties and methods in the IDbConnection interface.

What if you want to create a new .NET class that connects to Visual FoxPro data? You can declare a class and specify that it implements the IDbConnection interface.

In C#:

public class VfpConnection : IDbConnection

{

}

And in Visual Basic .NET

Public Class VfpConnection

   Implements IDbConnection

End Class 'VfpConnection

In C#, you specify a class implements an interface by placing a colon and the name of
the interface at the end of the first line of the class definition (: IDbConnection). This
is similar to the syntax for declaring a base class. In Visual Basic .NET, you add the Implements keyword and the name of the Interface on a line following the initial line of
the class definition.

If you compile this class “as is”, the compiler displays ten errors, one for each member of the IDbConnection interface. The error messages inform you that you are not living up to your contract. You have indicated you are implementing the IDbConnection interface, but you have not declared an implementation for each of these members in the VfpConnection class.

To satisfy the compiler, you must declare a property and method for each of the properties and methods in the IDbConnection interface. The following code provides a bare bones implementation of each interface member. If you were really creating this Visual FoxPro connection class, you would place implementation code in each property and method.


In C#:

public class VfpConnection : IConnection

{

  // Properties

  public string ConnectionString()

  {

       return null;

  }

  public string ConnectionTimeOut()

  {

       return null;

  }

  public string Database()

  {

       return null;

  }

  public ConnectionState State()

  {

       return new ConnectionState();

  }

 

  // Methods

  public IDbTransaction BeginTransaction()

  {

       return null;

  }

  public IDbTransaction BeginTransaction(System.Data.IsolationLevel level)

  {

       return null;

  }

  public void ChangeDatabase(string databaseName)

  {

  }

  public void Close()

  {

  }

  public IDbCommand CreateCommand()

  {

       return null;

  }

  public void Open()

  {

  }

}


And in Visual Basic .NET:

Public Class VfpConnection

    Implements IConnection

 

    ' Properties

    Public Function ConnectionString() As String _

        Implements IConnection.ConnectionString

        Return Nothing

    End Function 'ConnectionString

 

    Public Function ConnectionTimeOut() As String _

        Implements IConnection.ConnectionTimeOut

        Return Nothing

    End Function 'ConnectionTimeOut

 

    Public Function Database() As String _

        Implements IConnection.Database

        Return Nothing

    End Function 'Database

 

    Public Function State() As ConnectionState _

        Implements IConnection.State

        Return New ConnectionState()

    End Function 'State

 

 

    ' Methods

    Public Overloads Function BeginTransaction() As IDbTransaction _

        Implements IConnection.BeginTransaction

        Return Nothing

    End Function 'BeginTransaction

 

    Public Overloads Function BeginTransaction _

        (ByVal level As System.Data.IsolationLevel) As IDbTransaction _

        Implements IConnection.BeginTransaction

        Return Nothing

    End Function 'BeginTransaction

 

    Public Sub ChangeDatabase(ByVal databaseName As String) _

        Implements IConnection.ChangeDatabase

    End Sub 'ChangeDatabase

 

 

    Public Sub Close() _

        Implements IConnection.Close

    End Sub 'Close

 

 

    Public Function CreateCommand() As IDbCommand _

        Implements IConnection.CreateCommand

        Return Nothing

    End Function 'CreateCommand

 

    Public Sub Open() _

        Implements IConnection.Open

    End Sub 'Open

End Class 'VfpConnection

Implementing multiple interfaces

Although C# and Visual Basic .NET have a single implementation inheritance model (a class can be derived from only one base class), a class can implement multiple interfaces. For example, the following code declares a class named “Person” derived from the Component class that implements the IEmployee and IStockHolder interfaces.

In C#:

public class Person : Component, IEmployee, IStockHolder

{

}

In Visual Basic .NET:

Public Class Person

   Inherits Component

   Implements IEmployee, IStockHolder

End Class 'Person

Implementing interfaces with the Class View window in C#

If you are implementing an interface with more than a few members, it can be quite a bit of work implementing each interface member. If you are implementing an interface found in the .NET Framework class library, this usually entails copying and pasting into your code method signatures from a .NET Help topic detailing the interface.

If you are using C#, the Class View window provides a much easier way to implement an interface (this feature is not available in Visual Basic .NET). First, enter the class definition in the code-editing window. For example:

public class VfpConnection : IDbConnection

{

}

Next, right-click on the class definition and select Synchronize Class View from the shortcut menu. This opens the Class View window with the VfpConnection class highlighted in the tree view (Figure 7).

Figure 7. The Class Viewer displays the base class a class is derived from and any interfaces it implements.

If you expand the VfpConnection class node, you see a Bases and Interfaces node. If you expand this node, you see the base class of VfpConnection (Object) and the interfaces it implements (IConnection).

If you expand the IConnection interface node, you see a list of all properties and methods in the interface. To implement an interface member in the VfpConnection class, just right-click the member and select Add | Override from the shortcut menu (Figure 8).

Figure 8. In C#, you can use the Class Viewer to implement interfaces and methods from base classes.

After selecting this menu option, a method declaration is automatically added to the VfpConnection class. For example:

public System.Data.IDbTransaction BeginTransaction(System.Data.IsolationLevel

  level)

{

  return null;

}

Notice VS .NET fully qualifies class names in the code it generates. If you have already referenced the necessary namespace, you can remove the fully qualified name:

public IDbTransaction BeginTransaction(IsolationLevellevel)

{

  return null;

}

This is nice because it’s not just for interfaces. It also works for base class members. If you expand the base class node in the Class Viewer, right-click on a property or method, and select Add | Override from the shortcut menu, a declaration is added for the selected member.

Referencing classes by interface

In the section on abstract classes, you learned how to declare a variable that is the type of an abstract class, and then reference subclasses using the variable. This lets you write generic code that “programs to an interface, rather than an implementation”. This same principle holds true for interfaces.

You can declare a variable of a specific interface type, and then use the variable to reference any class that implements the interface. For example, the following code declares a variable of the type IDbConnection and stores a reference to a VfpConnection object. The subsequent lines of code show you can also store a reference to the SqlConnection, OracleConnection, and OleDbConnection object.

In C#:

IDbConnection Command = new VfpConnection();

Command = new SqlConnection();

Command = new OracleConnection();

Command = new OleDbConnection();

In Visual Basic .NET:

Dim Command As IDbConnection = New VfpConnection()

Command = New SqlConnection()

Command = New OracleConnection()

Command = New OleDbConnection()

When you reference an object through a particular interface, the only members you can access are the members of the specified interface—even if the class has other members in addition to those present in the interface. For example, given the above sample code, VS .NET IntelliSense on the Command object only displays the members shown in Figure 9.

Figure 9. When you reference an object via a specific interface, you only have access to the members of that interface.

Polymorphism and interfaces

In the previous sections, I used a data access interface (IDbConnection) to demonstrate the basics of .NET interfaces, because it provides a real world example of how you can achieve generic data access in your applications. The .NET Framework class library contains a number of generic data access interfaces implemented by concrete .NET data providers, such as the SQL Server data provider and the Oracle data provider. This is an example of polymorphism, because each interface can take many concrete forms.

However, if you instantiate the concrete classes that comprise these .NET data providers and use them directly, you are effectively hard-coding your data access. In contrast, if you reference these classes through the generic interfaces they implement, you are programming to an interface, rather than an implementation. This allows you to access data generically, giving your applications flexible data access.

Deciding between abstract classes and interfaces

When you get down to it, abstract classes and interfaces provide similar benefits. They both define interfaces that concrete classes can inherit, providing the flexibility that comes with object-oriented polymorphism. So, how do you decide whether to use an abstract class or an interface in a given situation?

There are a variety of criteria you can use, but one of the more practical considerations is whether or not the classes implementing the common behavior are related. If you are defining functionality for a family of related classes (as with the application settings classes discussed earlier in this chapter), you can use an abstract class. If the common functionality needs to be implemented across unrelated classes, then use interfaces.

For more information, see the .NET Help topic “Recommendations for Abstract Classes vs. Interfaces”.

Instance and static (Shared) members

There are two main types of class members—instance and static (Shared in VB .NET) members. Instance members are the default type of member in both C# and Visual Basic .NET. They are fields, properties, methods, and so on, which belong to each instance of a class. For example, the following code declares a class named InstanceMemberDemo with a public instance field named Counter. The class also contains an IncrementCounter method that increments the Counter field and displays its value in a MessageBox.

In C#:

public class InstanceMemberDemo

{

  public int Counter = 1;

 

  public void IncrementCounter()

  {

       this.Counter++;

       MessageBox.Show("Counter = " + Counter);

  }

}

And in Visual Basic .NET:

Public Class InstanceMemberDemo

    Public Counter As Integer = 1

 

 

    Public Sub IncrementCounter()

        Me.Counter += 1

        MessageBox.Show(("Counter = " & Counter.ToString()))

    End Sub 'IncrementCounter

End Class 'InstanceMemberDemo

Now take a look at the following code that creates and manipulates instances of this class.

In C#:

InstanceMemberDemo InstanceDemo1 = new InstanceMemberDemo();

InstanceMemberDemo InstanceDemo2 = new InstanceMemberDemo();

InstanceDemo1.IncrementCounter();

InstanceDemo2.IncrementCounter();

And in Visual Basic .NET:

Dim InstanceDemo1 As New InstanceMemberDemo()

Dim InstanceDemo2 As New InstanceMemberDemo()

InstanceDemo1.IncrementCounter()

InstanceDemo1.IncrementCounter()

The first two lines of code create instances of the class named “InstanceDemo1” and “InstanceDemo2”. At this point, both Counter fields are set to 1. After running the third line of code that calls the IncrementCounter method of InstanceDemo1, the Counter field in InstanceDemo1 is set to two, and the Counter field in InstanceDemo2 is still set to one. After running the fourth line of code that calls the IncrementCounter method of InstanceDemo2, both instances have their Counter field set to two (Figure 10). This is because each instance has its own copy of the variable. This is the way Visual FoxPro works. All members of Visual FoxPro classes are instance members.

Figure 10. Each instance of a class has its own copy of all instance members

Now you’re ready to look at static members. Static members belong to the class itself, rather than to each instance of the class. Only one copy of a static member exists in an application regardless of how many instances of the class are created.

To illustrate this point, I’ll change the Counter field in the previous example to a static member. Notice I also changed the way I reference the Counter field. Rather than referencing it as this.Counter (C#) or Me.Counter (VB .NET), it must simply be referenced as Counter. When you think about it, this makes sense. The keywords this and Me refer to an instance of an object, and in this scenario, the Counter field does not belong to instances of the class, it belongs to the StaticMethodDemo class itself.

In C#:

public class StaticMemberDemo

{

  public int Counter = 1;

 

  public void IncrementCounter()

  {

       Counter++;

       MessageBox.Show("Counter = " + Counter);

  }

}

And in Visual Basic .NET

Public Class StaticMemberDemo

    Public Shared Counter As Integer = 1

 

 

    Public Sub IncrementCounter()

        Counter += 1

        MessageBox.Show(("Counter = " & Counter.ToString()))

    End Sub 'IncrementCounter

End Class 'StaticMemberDemo

Now, take a look at the code that instantiates and manipulates these classes.

In C#:

StaticMemberDemo StaticDemo1 = new StaticMemberDemo();

StaticMemberDemo StaticDemo2 = new StaticMemberDemo();

StaticDemo1.IncrementCounter();

StaticDemo2.IncrementCounter();

And in Visual Basic .NET:

Dim StaticDemo1 As New StaticMemberDemo()

Dim StaticDemo2 As New StaticMemberDemo()

StaticDemo1.IncrementCounter()

StaticDemo2.IncrementCounter()

When the first two lines of code are executed, the Counter field is set to its initial value of one, but remember this value is stored at the class level, rather than with each instance of the class. When the IncrementCounter method is called on the StaticDemo1 object, the Counter field is set to two. When the IncrementCounter method is called on the StaticDemo2 object, the Counter field is set to three (Figure 11).

Figure 11. Static members belong to the class itself rather than to each instance of the class.

Because this is a static field, the field and its value are stored at the class level. When you reference the Counter field from within either object, it points back to the field and value stored at the class level.

Text Box: ¥

Static properties and fields are similar to global variables in Visual FoxPro.

Referencing static class members

As you’ve already seen, when you reference a static class member from within an instance of a class, you don’t use this or Me. So, how do you access static members from outside the class? You do this by using the syntax “ClassName.Member”. For example, look at the following code that references the static Counter field declared in the StaticMemberDemo class.

In C#:

MessageBox.Show("Counter: " + StaticMemberDemo.Counter);

And in Visual Basic .NET:

MessageBox.Show(("Counter: " & StaticMemberDemo.Counter.ToString()))

It may take a little while to wrap your mind around static members, because they’re new to Visual FoxPro developers, but in reality I’ve used them extensively in the sample code shown so far. For example, look at the previous code sample calling “MessageBox.Show”. Notice I never instantiated an instance of the MessageBox class—I simply called its Show method directly. As you might guess, the Show method of the MessageBox class is a static member. If you look at the .NET Help topic “MessageBox Members”, you’ll see the Show method is marked with a yellow “S”, to indicate it is static (“Shared” in VB .NET).

Figure 12. The .NET Help file displays a yellow “S” next to all static class members.

This convention is used throughout the .NET Help file, so you can easily determine whether members are instance or static.

Events and delegates

Events happen. Objects raise events and other objects respond to them. Visual FoxPro has traditionally been weak in the area of events. Although you can write code that responds to events in VFP, you can’t raise your own custom events as in Visual Basic 6. Although Visual FoxPro’s event model is limited, it is simple and straightforward. All you have to do is place code in an event, and it executes when the event fires.

In contrast, C# and VB .NET both have full support for raising and responding to events. The .NET event model is more robust and flexible than Visual FoxPro’s, but with this flexibility comes a small learning curve in understanding and implementing events.

The .NET event model is based on the object-oriented Observer design pattern. This model involves three main entities:

·         An event transmitter

·         An event receiver

·         A delegate

The event transmitter is responsible for raising the event, which is usually fired as a result of an action. For example, if the user clicks a button, the button raises its Click event. An event receiver, or handler, is an object that captures and responds to an event. As with the Observer design pattern, the event transmitter does not know which object or specific method on an object handles the event it raises. Due to this loose coupling, you need an object to act as an intermediary between the event transmitter and event receiver. This object is the delegate.

Delegates

A delegate is a class that holds a reference to an object method. This is something new to VFP developers. In Visual FoxPro, there are object references, but a delegate holds a reference to a single method on an object!

Each delegate class can only refer to methods matching one specific signature. When
you define a delegate class, you specify the signature of the methods it references. For example, the .NET Framework class library contains a generic delegate class for handling events called “EventHandler”.

Here is its definition in C#:

delegate void EventHandler(object sender, EventArgs e);

And in Visual Basic .NET:

Delegate Sub EventHandler(sender As Object, e As EventArgs)

This code is a bit different from how you declare “normal” classes. When you declare a delegate, you specify an associated method signature. The EventHandler delegate has a signature with two parameters. The first parameter is an “object” type. It holds a reference to the event transmitter. The second parameter is an “EventArgs” type. It is used to pass any information from the event transmitter to the event receiver. The EventArgs class is the base class for all classes passing data from events. It represents an event that does not pass any data.

If your event transmitter does not pass any information to the event receiver, you can use this generic EventHandler delegate class with its object and EventArgs parameters. Otherwise, you must create your own custom delegate class.

An events example

This section takes you through creating your own custom event transmitter, delegate, and event receiver demonstrating how to localize your application text by means of .NET events (“localizing” is the process of translating text to another language). To do this, you will create a LanguageMgr object that raises a Localize event. You will also create a MyLabel class, which is a Windows Forms label acting as the event receiver, or handler, responding to the Localize event. Finally, you will create a  “LocalizeDelegate” class to act as an intermediary between the language manager object and the label object (Figure 13).

Figure 13. In the .NET event model, there are three primary objects; the event transmitter, the delegate, and the event receiver.

Creating a delegate

In this example, you need to create a delegate to act as an intermediary between the LanguageMgr and the MyLabel objects. When the LanguageMgr class raises its Localize event, it passes a single integer argument to any objects that handle the event. This integer represents the primary key of a record in a Language table. Because this parameter needs to be passed, you cannot use the .NET Framework’s generic EventArgs class when declaring this delegate (it represents an event that does not pass any data).

So, here is the event arguments definition in C#:

public class LocalizeEventArgs : EventArgs

{

  // The language property and associated field

  private int language;

  public int Language

  {

       get { return language; }

       set { language = value; }

  }

 

  // The constructor

  public LocalizeEventArgs(int language)

  {

       this.Language = language;

  }

}

And in Visual Basic .NET:

Public Class LocalizeEventArgs

    Inherits EventArgs

    ' The language property and associated field

    Private _language As Integer

 

    Public Property Language() As Integer

        Get

            Return _language

        End Get

        Set(ByVal Value As Integer)

            _language = Value

        End Set

    End Property

 


    ' The constructor

    Public Sub New(ByVal language As Integer)

        Me.Language = language

    End Sub 'New

End Class 'LocalizeEventArgs

Now, you’re ready to declare a delegate that uses this new LocalizeEventArgs class. The following code declares a delegate named LocalizeDelegate that holds references to methods with the following signature:

·         An object parameter

·         A LocalizeEventArgs parameter

·         No return value

Here is the delegate declaration in C#:

public delegate void LocalizeDelegate(object sender, LocalizeEventArgs e);

And in Visual Basic .NET:

Public Delegate Sub LocalizeDelegate(ByVal sender As Object, _

    ByVal e As LocalizeEventArgs)

If you try to make the delegate hold a reference to an object method that does not have this signature, you will get a compiler error.

Creating an event transmitter

Now it’s time to create an event transmitter. The following code declares a class named LanguageMgr that contains a custom Localize event. This is the part you can’t do in Visual FoxPro. You are allowed to hook into existing VFP events, but you can’t create your own custom events.

This class also contains a method named SetNewLanguage that accepts a single integer parameter specifying the unique id of a language. This method instantiates a LocalizeEventArgs class and passes the language integer in the class constructor. It then passes the LocalizeEventArgs object to its OnLocalize method.

In C#, the OnLocalize method first checks if the Localize event is null. If no delegates are registered with the Localize event, then it will be null. If it’s not null, the method passes the event arguments object to the Localize event. In Visual Basic .NET, you don’t need to perform this check. If no delegates have been registered with the event, you can raise the event without throwing an exception.

In C#:

public class LanguageMgr

{

  ///  Specifies a custom event member that is of the type LocalizeDelegate

  public event LocalizeDelegate Localize;

 

  public void SetNewLanguage(int language)

  {

       LocalizeEventArgs e = new LocalizeEventArgs(language);

       this.OnLocalize(e);

  }

 

  /// This method raises the event by invoking the delegates

  protected virtual void OnLocalize(LocalizeEventArgs e)

  {

       if (Localize != null)

       {

          // Invoke the delegates, specifying this class as the sender

          Localize(this, e);

       }

  }

}

And in Visual Basic .NET:

Public Class LanguageMgr

    '  Specifies a custom event member that is of the type LocalizeDelegate

    Public Event Localize As LocalizeDelegate

 

    ' This method raises the event by invoking the delegates

    Protected Overridable Sub OnLocalize(ByVal e As LocalizeEventArgs)

        RaiseEvent Localize(Me, e)

    End Sub 'OnLocalize

 

    Public Sub SetNewLanguage(ByVal language As Integer)

        Dim e As New LocalizeEventArgs(language)

        Me.OnLocalize(e)

    End Sub 'SetNewLanguage

End Class 'LanguageMgr

When the Localize event fires, it passes the LocalizeEventArgs object to all delegates registered with the event. In the next section, you will create an event handler object and register it with the event transmitter using the custom delegate class.

Text Box: ¥

You may wonder why the LanguageMgr class has a separate method called “OnLocalize” containing the code that raises the event. Why not just put this code directly in the SetNewLanguage Method? Because placing this code in a separate method allows subclasses of LanguageMgr to handle the event by overriding this method. For details, see the “Overriding events defined in the .NET Framework” section later in this chapter

Creating the event handler

Now you’re ready to create the event handler. The following code declares a class named “MyLabel” derived from the Windows Forms Label class.


In C#:

public class MyLabel : Label

{

  public void Localize(object sender, LocalizeEventArgs e)

  {

       // Localize the label's text

       MessageBox.Show("Localizing the control to language: " +

          e.Language);    

  }

}

And in Visual Basic .NET:

Public Class MyLabel

    Inherits Label

 

    Public Sub Localize(ByVal sender As Object, _

        ByVal e As LocalizeEventArgs)

        ' Localize the label's text

        MessageBox.Show(("Localizing the control to language: " & _

            e.Language.ToString()))

    End Sub 'Localize

End Class 'MyLabel

Notice the Localize method has the same signature as defined in the LocalizeEventArgs delegate. This allows you to use LocalizeDelegate to hold a reference to this method as shown in the next section.

Registering the event handler with the event transmitter

Now that you’ve defined all the pieces, you’re ready to register the MyLabel.Localize event handler method with the event transmitter.

The following code first instantiates the LanguageMgr class, which is the event transmitter. Next, it instantiates the MyLabel class, which is the event handler. The third line of code is a bit different in C# versus VB .NET. In C#, this line of code instantiates an instance of the custom LocalizeDelegate, passing a reference to the LabelObj’s Localize method in its constructor. It uses the += operator to register the delegate with the Localize event of the language manager.

In C#:

// Instantiate the event transmitter

LanguageMgr LangMgr = new LanguageMgr();

                    

// Instantiate the event handler

MyLabel LabelObj = new MyLabel();

 

// Register the delegate with the event

LangMgr.Localize += new LocalizeDelegate(LabelObj.Localize);

 

// Fire the Localize event by setting a new language

LangMgr.SetNewLanguage(1);

In Visual Basic .NET, you don’t need to explicitly “wire-up” to a delegate, because it’s done automatically for you. The third line of VB .NET code uses the AddHandler statement to associate the Localize event with the LabelObj.Localize event handler:

' Instantiate the event transmitter

Dim LangMgr As New LanguageMgr()

 

' Instantiate the event handler

Dim LabelObj As New MyLabel()

 

' Register the delegate with the event

AddHandler LangMgr.Localize, AddressOf LabelObj.Localize

 

' Fire the Localize event by setting a new language

LangMgr.SetNewLanguage(1)

For details on how this works in VB .NET, see the .NET Help topic “Delegates and the AddressOf Operator”.

 The last line of code triggers the language manager’s Localize event, by calling the SetNewLanguage method. The UML sequence diagram in Figure 14 shows the basic message flow between the LanguageMgr, LocalizeDelegate, and MyLabel objects. Notice the event transmitter (LanguageMgr) never talks directly to the event handler (MyLabel). All communication takes place through the delegate (LocalizeDelegate).

Figure 14. This diagram demonstrates the basic message flow between event transmitter, delegate, and event handler objects.

Overriding events defined in the .NET Framework

In the .NET Help topic “Event Usage Guidelines”, Microsoft recommends creating a protected, virtual method for raising an event so subclasses can handle the event by overriding the method.

Microsoft followed this standard when defining events in the .NET Framework classes. This means you can override events defined in the .NET Framework by overriding the event’s associated OnEventName method. For example, the .NET Help topic “Overriding the Paint Event” contains the following sample code for overriding the Paint event of the System.Windows.Forms.Control class.

In C#:

public class FirstControl : Control{

Text Box:    protected override void OnPaint(PaintEventArgs e) 
      // Call the OnPaint method of the base class.
      base.OnPaint(e);

   {

     

      // Call methods of the System.Drawing.Graphics object.

      e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor),

       ClientRectangle);

   }

}

In Visual Basic .NET:

Public Class FirstControl

   Inherits Control

Text Box:    Protected Overrides Sub OnPaint(e As PaintEventArgs)
      ' Call the OnPaint method of the base class.
      MyBase.OnPaint(e)

  

      ' Call methods of the System.Drawing.Graphics object.

      e.Graphics.DrawString(Text, Font, New SolidBrush(ForeColor), _

        RectangleF.op_Implicit(ClientRectangle))

   End Sub

End Class

This code defines a class named “FirstControl” derived from the .NET Framework’s System.Windows.Forms.Control class. The code shown in grey overrides the OnPaint method of the Control class. Notice the method first calls the base class’s OnPaint method before it performs its own processing. You must call the OnEventName method of the base class in this way to ensure registered delegates receive the event.

Event handling using WithEvents in Visual Basic .NET

Although you can use the same technique for creating events in Visual Basic .NET as in C#, VB .NET provides an alternate method using the WithEvents and Handles keywords. This methodology isn’t as flexible as the one described in the previous section, but if your needs are more basic, it provides an easy way to raise and respond to events in VB .NET.

A great place to see WithEvents at work is the VB .NET user interface code. For example, the Visual Basic .NET source code for this book has a Windows Form code-behind file named SampleCodeForm.vb. A search for the phrase “WithEvents” takes you to several form-level variables declared using the WithEvents keyword. The following code declares a variable named “cmdClose” of the type System.Windows.Forms.Button. The WithEvents keyword specifies this variable contains an object that is a source of events.

Friend WithEvents cmdClose As System.Windows.Forms.Button

Further down in the source code is the following handler code for this event. The Handles keyword specifies this method handles the cmdClose.Click event.

Private Sub cmdClose_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles cmdClose.Click

    Me.Close()

End Sub

This is extremely easy and straightforward, eliminating the need to work with delegates directly—Visual Basic .NET handles delegate registration for you behind the scenes. Although WithEvents can be used for many VB .NET event handling situations, the .NET Help topic “WithEvents and the Handles Clause” lists the following restrictions on the use of WithEvents variables:

·         You cannot use a WithEvents variable as a generic object variable. You must specify a class name when you declare the variable.

·         You cannot use WithEvents to declaratively handle shared events, because they are not tied to an instance that can be assigned to a WithEvents variable.

·         You cannot create arrays of WithEvents variables.

·         WithEvents variables allow a single event handler to handle one or more kind of event, or one or more event handlers to handle the same kind of event.

For more information on using WithEvents, see the .NET Help topic “Writing
Event Handlers”.

Event handling made easy in Visual Studio .NET

As shown in the previous section, creating your own custom events, event handlers, and delegates takes a bit of effort. Fortunately, Visual Studio .NET makes plugging into events much easier than this. Take a quick look at a simple example.

Say you have a button on a Windows form named “cmdClose”. If you open this form in design mode in Visual Studio .NET and double-click the button, it automatically adds an event handler method behind the scenes. You can add custom code to this method that automatically executes when the event fires. For example, the following event handler method contains code to close the form.

In C#:

private void cmdClose_Click(object sender, System.EventArgs e)

{

  this.Close();

}


And in Visual Basic .NET:

Private Sub cmdClose_Click(ByVal sender As System.Object, _

    ByVal e As System.EventArgs) Handles cmdClose.Click

    Me.Close()

End Sub

However, this code is only half the story. If you expand the “Windows Form Designer Generated Code” section, you will see the following code.

In C#:

this.cmdClose.Click += new System.EventHandler(this.cmdClose_Click);

In Visual Basic .NET:

Friend WithEvents cmdClose As System.Windows.Forms.Button

This code should look somewhat familiar! As discussed previously, the C# code instantiates a delegate of the type EventHandler, passing a reference to the cmdClose_Click event handler method. In Visual Basic .NET, the code is even easier, because there are no delegates involved. The WithEvents keyword declares the cmdClose variable contains an object that is a source of events.

Double-clicking a user interface element in Visual Studio .NET automatically creates an event handler for the object’s default event. So, how do you create event handlers in VS .NET for other events?

Automatically creating event handlers in C#

If you’re working with C#, you automatically create event handlers in VS .NET as follows:

1.       From within Visual Studio .NET, select the object in design mode.

2.       Go to the Properties Window (press F4 if it’s not visible).

3.       Select the Events button at the top of the dialog (the button with the lightning bolt shown in Figure 15).

4.       Double-click on the desired event and VS .NET automatically places an event handling method in the form for you.

Figure 15. In C#, you can see an object’s events by selecting it in design mode and clicking the Events button in the Properties Window.

Text Box: ¥

If you accidentally double-click on an event in the Properties Window, or on a user interface element in design mode, you can easily remove the event handling code automatically added to the form by right-clicking on the event in the Properties Window and selecting Reset from the shortcut menu.

Automatically creating event handlers in Visual Basic .NET

If you’re working with Visual Basic .NET, you automatically create event handlers in VS .NET as follows:

1.       In Visual Studio .NET’s code editor, select the desired WithEvents variable from the combo box at the upper left of the code-editing window.

2.       Choose the event you want to handle from the combo box at the upper right of the code-editing window.

3.       VS .NET automatically places an event-handling method in the file for you.


Structures

Structures are similar to classes, but rather than being reference types, they are value types (For more information, see the “Value and reference types” section in Chapter 3, “Introduction to C#). Because they are value types, they have a slight speed advantage over classes.

Structures are defined in C# as shown in this sample:

public struct StructureDemo

{

  public string FirstName;

  public string MiddleName;

  public string LastName;

 

  public string GetName()

  {

       return FirstName + " " +

          MiddleName + " " +

          LastName;

  }

}

And in Visual Basic .NET:

Public Structure StructureDemo

    Public FirstName As String

    Public MiddleName As String

    Public LastName As String

 

 

    Public Function GetName() As String

        Return FirstName & " " & MiddleName & " " & LastName

    End Function 'GetName

End Structure 'StructureDemo

Structures can:

·         Have properties and methods.

·         Raise and handle events.

·         Implement interfaces.

Structures cannot:

·         Have subclasses.

·         Have protected members (this makes sense because they can’t have subclasses).

Copying structures

Because structures are value types, you can copy values from one structure to another by simply assigning one structure variable to another. For example, the following code instantiates two instances of the StructureDemo structure. It sets the properties of the first instance, and then assigns the first structure variable to the second variable. When the last line is executed, it displays a message showing the second structure now has the same property values as the first.

In C#:

StructureDemo StructDemo1 = new StructureDemo();

StructureDemo StructDemo2 = new StructureDemo();

 

StructDemo1.FirstName = "Alexander";

StructDemo1.MiddleName = "James";

StructDemo1.LastName = "McNeish";

 

StructDemo2 = StructDemo1;

MessageBox.Show("Structure2 Name: " + StructDemo2.GetName(), "Structure demo");

In Visual Basic .NET:

Dim StructDemo1 As New StructureDemo()

Dim StructDemo2 As New StructureDemo()

 

StructDemo1.FirstName = "Alexander"

StructDemo1.MiddleName = "James"

StructDemo1.LastName = "McNeish"

 

StructDemo2 = StructDemo1

MessageBox.Show("Structure2 Name: " & StructDemo2.GetName(), "Structure demo")

Deciding between classes and structures

Structures are value types, so their data is stored on the stack rather than the heap. This means you should use a structure only if the object you create has a small instance size. Objects large in size should be classes.

One common use for structure is as a device for passing parameters. If you have several parameters you need to pass to a method, you can create a structure with a different property to hold each parameter value.

Text Box: ¥

Behind the scenes, all value data types such as Boolean, Byte, Int32, and Decimal are actually structures!

Attributes

.NET attributes allow you to place extra descriptive information in your code that the compiler turns into metadata within your project’s assembly. Attributes are useful at a variety of levels, such as an assembly, a class, or a class member. They can also be applied for a variety of reasons, as you’ll see in this section.

When you create a new project in Visual Studio .NET, a file named Assembly.cs (or Assembly.vb) is automatically added to your project. This file contains attributes that apply to the entire assembly.

Here is an excerpt from a C# Assembly.cs file:

[assembly: AssemblyTitle("")]

[assembly: AssemblyDescription("")]

[assembly: AssemblyCompany("")]

[assembly: AssemblyProduct("")]

[assembly: AssemblyCopyright("")]

[assembly: AssemblyTrademark("")]

And from a Visual Basic .NET Assembly.vb file:

<Assembly: AssemblyTitle("")>

<Assembly: AssemblyDescription("")>

<Assembly: AssemblyCompany("")>

<Assembly: AssemblyProduct("")>

<Assembly: AssemblyCopyright("")>

<Assembly: AssemblyTrademark("")>

The keyword assembly indicates these attributes are applied at the assembly level. If you enter a description between the double quotes for each attribute, the compiler turns the description into metadata within your project’s assembly. If you want a quick way to view this attribute information, you can use the Intermediate Language Disassembler (IL DASM) discussed in Chapter 1, “Introduction to .NET”. This tool is located in the FrameworkSDK\ Bin folder below the directory on your machine containing the .NET Framework.

To launch the disassembler, just double-click the ildasm.exe file. To open an assembly, select File | Open from the menu. Use the Open dialog for navigating to and opening the desired assembly. Once the assembly is open, double-click the MANIFEST node (Figure 16).

Figure 16. You can view assembly-level attributes using the Intermediate Language Disassembler.

This opens a window containing IL code, along with comments displaying the attribute text. Although this is interesting, the most practical way to examine attributes is at runtime by means of reflection. For more information, see the “Reflection” section later in this chapter.

Believe it or not, attributes are actually classes. The .NET Framework class library contains a few hundred attribute classes for all occasions! All attribute classes are derived from the System.Attribute class.

Here is an example of an attribute applied at the method level. The Obsolete attribute allows you to specify a method is going to eventually be removed.

In C#:

public class AttributeDemo

{

  [Obsolete("Method will be removed in the next version")]

  public void YourFavoriteMethod()

  {

  }

}

And in Visual Basic .NET:

Public Class AttributeDemo

 

    <Obsolete("Method will be removed in the next version")> _

    Public Sub YourFavoriteMethod()

    End Sub 'YourFavoriteMethod

End Class 'AttributeDemo

If any code in your application calls this method, the compiler displays the warning message specified in the attribute declaration:

..YourFavoriteMethod()' is obsolete: 'Method will be removed in the next version'

You will see more examples of how attributes are used in Chapter 12, “XML Web Services”. For more general information, see the .NET Help topic “Extending Metadata Using Attributes”. For information on creating your own custom attributes, see the .NET Help topic “Writing Custom Attributes”.

Indexers

The use of Indexers is an object-oriented convenience feature that allows you to access an object as if it is an array. Indexers are similar to properties, but their accessors take parameters.

For example, the following code declares a class named “Address” with three public string fields named “Street”, “CityState”, and “Zip”. Indexers give users the option of accessing instances of the class as an array, with each of these fields representing a different row in the array. The code highlighted in grey is the indexer declaration. This particular indexer specifies instances of the Address class can be treated as a one-dimensional array with an int index.

In C#:

public class Address

{

  public string Street, CityState, Zip;

 

Text Box: 	public string this[int index]

  /// Indexer for the Street, CityState & Zip fields

  {

    get

    {

       switch (index)

       {

         case 0:

           return Street;

         case 1:

           return CityState;

         case 2:

           return Zip;

         default:

           throw new IndexOutOfRangeException(

              "Invalid Address element specified " + index);

       }

    }

    set

    {

       switch (index)

       {

         case 0:

           Street = value;

           break;

         case 1:

           CityState = value;

           break;

         case 2:

           Zip = value;

           break;

         default:

           throw new IndexOutOfRangeException(

              "Invalid address element specified " + index);

       }

    }

  }

 

  /// Initialize the address properties

  public Address()

  {

    this[0] = "952 Rockledge Drive";

    this[1] = "Charlottesville, VA";

    this[2] = "22903";

  }

}

And in Visual Basic .NET:

Public Class Address

  Public Street, CityState, Zip As String

 

   ' Indexer for the Street, CityState & Zip fields

   Default Public Property Item(ByVal index As Integer) As String

     Get

       Select Case index

         Case 0

          Return Street

         Case 1

          Return CityState

         Case 2

          Return Zip

         Case Else

          Throw New IndexOutOfRangeException("Invalid element specified " & _

          index.ToString())

       End Select

   End Get

 

   Set(ByVal Value As String)

     Select Case index

       Case 0

         Street = Value

       Case 1

                    CityState = Value

       Case 2

         Zip = Value

       Case Else

         Throw New IndexOutOfRangeException("Invalid element specified " & _

         index.ToString())

     End Select

   End Set

  End Property

 

  ' Initialize the address properties

  Public Sub New()

   Me(0) = "952 Rockledge Drive"

   Me(1) = "Charlottesville, VA"

   Me(2) = "22903"

  End Sub 'New

End Class 'Address

Within the body of the indexer declaration the get and set accessors use the integer index to access values in the Street, CityState, and Zip fields.

Here is an example of code used to access an instance of this class as an array.

In C#:

Address AddressObj = new Address();

MessageBox.Show("Indexer address: \n\n" +

  AddressObj[0] + "\n" +

  AddressObj[1] + "\n" +

  AddressObj[2] + "\n");

And in Visual Basic .NET:

Dim AddressObj As New Address()

MessageBox.Show(("Indexer address: " & ControlChars.Lf & _

ControlChars.Lf & AddressObj(0) & ControlChars.Lf & _

AddressObj(1) & ControlChars.Lf & AddressObj(2) & ControlChars.Lf))

Notice how similar this is to accessing an array. You simply reference the object name followed by brackets or parentheses containing the index of the element you want to access.

Garbage Collection

If the phrase “memory leak” makes your skin crawl, you’ll be happy to learn about the .NET Framework’s garbage collector. In Visual FoxPro, you have to be extremely careful about cleaning up after objects when you are done using them. When you release an object, its Destroy method is fired, and the memory allocated for it is freed up—if all goes well.

In .NET, as you instantiate objects, the runtime allocates memory for them on the heap. However, rather than having to worry about releasing objects yourself, the .NET runtime takes care of this for you. Periodically, .NET’s garbage collector checks for objects in the heap no longer being used and reclaims their memory.

Because the garbage collector periodically releases objects from memory on an “as-needed” basis, you can’t count on an object being released at a specific time. Although it may take a while to get used to giving up this control, you’ll find this works well in most cases.

Dispose methods

Because there is a delay between the time you are finished with an object and when the garbage collector physically releases it, if your object uses system resources such as database connections, you may want to provide clients with a method they can call to release these resources whenever they choose.

The .NET Framework’s IDisposable interface supplies a Dispose method that consumers of your object can call to release resources acquired by your object. For more information on implementing the IDisposable interface, see the .NET Help topics “Implementing a Dispose Method” and “Initialization and Termination of Components”.

Destructors and Finalize methods

All classes inherit the Finalize method from the System.Object class. This method automatically fires when the garbage collector releases an object. The Finalize method also automatically calls the Dispose method on classes that implement the IDisposable interface. Because this is the case, you should always call GC.SuppressFinalizeMethod from your Dispose method.

Normally, you don’t have to worry about the Finalize method. However, if your object is using unmanaged resources, you may want to add code to your object to clean up these resources when the object is destroyed.

If you’re using C#, you accomplish this by declaring a destructor method for your class. In the same way C# constructors are named the same as their containing class, destructors are also given the name of their containing class preceded by a tilde (~). For example, if you have a class named “Customer”, you would create a destructor as follows:

~ Customer()

{

  // Cleanup Code

}

C# destructors are designed to automatically call the object’s Finalize method.

If you’re using Visual Basic .NET, you don’t create destructors as in C#. Instead, you create an override of the Finalize method in your class, placing the necessary cleanup code directly in this method. For example:


Protected Override Sub Finalize()

  ' Cleanup Code

  MyBase.Finalize()

End Sub

 Although placing cleanup code in destructor or Finalize methods is useful when you need it, be forewarned that doing so can impact application performance. Also, as mentioned previously, you can’t guarantee when the Finalize method of an object will execute, so do not rely on the timing of any code placed in the Finalize method. For details, see the .NET Help topics “Finalize Methods and Destructors” and “Automatic Memory Management”.

C#’s using statement

C#’s using statement has a convenience feature not available in Visual Basic .NET. In this context, using is something completely different than when you are “using” a namespace. The using statement provides a more automatic way of calling the Dispose method of an object. For example, the using statement in the code shown below instantiates a class named “UnmanagedResource”. Within the curly braces of the using statement, you place code that uses the MyRes object. When the object loses scope at the bottom of the using statement, the object’s Finalize method is automatically called.

public class UsingDemo

{

  public void MyMethod()

  {

       using (UnmanagedResource MyRes = new UnmanagedResource())

       {

          // Use the MyRes object

       }

  }

}

Note the class you instantiate in the using statement must implement the IDisposing interface.

Operator Overloading

The C# language has an advanced object-oriented feature called operator overloading that is not available in Visual Basic .NET. Earlier in this chapter, you learned about method overloading—an object-oriented technique that allows you to create multiple methods with the same name.

Operator overloading allows you to do something similar with operators. You can provide new meaning for operators such as +, -, !, ++, and --, by defining static methods in classes using the operator keyword.

Although this is an interesting feature, it’s probably not one you will use often. Rather than going into detail here, you can check out two good examples of operator overloading in the .NET Help topic “Operator Overloading Tutorial”. For a list of operators that can be overloaded, see the .NET Help topic “Overloadable Operators”.


Reflection

In Visual FoxPro, there is a certain freedom and flexibility that comes from specifying the class of an object be instantiated at run time. For example, I use an abstract factory in my Visual FoxPro applications allowing me to data drive my class instantiation. I call the abstract factory’s GetClassName method, passing a token specifying the kind of class I want to instantiate. The abstract factory looks up this token in a table, returning the name of the class I should use. I then pass the class name to the CREATEOBJECT command, which instantiates an object from the specified class.

lcClassName = goApp.oFactory.GetClassName("SecurityMgr")

loSecurityMgr = CREATEOBJECT(lcClassName)

If you are using the new command in C# or in Visual Basic .NET (assuming Option Strict is “On”), you are required to specify the type of the class you will instantiate at compile time.

In C#:

Customer CustomerObj = new Customer();

In Visual Basic .NET:

Dim CustomerObj As New Customer()

Fortunately, there is a way to achieve Visual FoxPro’s object creation flexibility in C# and VB .NET by using reflection. .NET reflection supplies objects that encapsulate assemblies, modules, and classes, allowing you to dynamically create an instance of a class. Some other things you can do with reflection are:

·         Load assemblies and modules.

·         Retrieve information about class constructors.

·         Retrieve information about class methods and invoke them.

·         Retrieve information about class fields and get/set their values.

·         Retrieve information about class events and add/remove event handlers.

·         Retrieve information about class properties and get/set their values.

·         Retrieve information about method parameters.

·         Generate MSIL code on the fly.

Accessing type information

You get type (class) information from assemblies already loaded in memory by calling the static Type.GetType method. This and other reflection methods return a System.Type object you use to derive information about classes.

For example, the following code calls the GetType method, passing a string containing the fully qualified name of the Client class declared in this chapter’s sample code.

In C#:

Type ClientType = Type.GetType("HW.NetBook.Samples.Client");

In Visual Basic .NET:

Dim ClientType As Type = Type.GetType("HW.NetBook.Samples.Client")

After running this code, you can examine the ClientType object to discover information about the Client class. Because it is a Type object, it has a number of properties and methods you can use to get information about the Client class. Table 4 lists some of the more interesting properties and methods.

Table 4. Type objects have a good number of properties and methods you can use to find out information about classes.

Properties

Description

Assembly

The assembly where the type is declared.

BaseType

The type from which the current type directly inherits.

FullName

The fully qualified name of the type, including the namespace.

GUID

The GUID associated with the type.

IsAbstract

Specifies if the type is abstract.

IsClass

Specifies if the type is a class.

IsCOMObject

Specifies if the type is a COM object.

IsEnum

Specifies if the type is an enumeration.

IsInterface

Specifies if the type is an interface.

Name

The name of the type

Namespace

The namespace of the type

UnderlyingSystemType

Specifies the .NET Framework base class the type is based on. Even if you have several layers of inheritance in your class hierarchy, this property displays the first .NET Framework base class in the hierarchy.

 

Methods

Description

FindInterfaces

Returns an array of Type objects representing a list of interfaces implemented or inherited by the current type.

FindMembers

Returns an array of FilterMember objects of the specified member type (i.e. constructor, property, event, method).

GetEvent

Gets a specific event inherited or declared by the current type.

GetField

Gets a specific field of the current type.

GetInterface

Gets a specific interface implemented or inherited by the current type.

GetMember

Gets the specified members of the current type.

GetMethod

Gets a specific method of the current type.

GetProperty

Gets a specific property of the current type.

InvokeMember

Invokes a specific member of the current type.

 

To obtain information about types located in assemblies that are not loaded, you can use the Assembly.GetType or Assembly.GetTypes methods.

Late binding with reflection

When the type of an object is determined at run time rather than compile time, this is known as late binding. This is the type of binding used with Visual FoxPro’s CREATEOBJECT command. Following is some code that simulates CREATEOBJECT.

The code declares a class you instantiate using late binding.

In C#:

public class Message

{

  public void ShowMessage(string msg)

  {

       MessageBox.Show(msg, "Message class");

  }

}

And in Visual Basic .NET:

Public Class Message

 

    Public Sub ShowMessage(ByVal msg As String)

        MessageBox.Show(msg, "Message class")

    End Sub 'ShowMessage

 

End Class 'Message

The following code instantiates the Message class and calls its ShowMessage method.

In C#:

// Get the type to use from the assembly.

Type MessageType = Type.GetType("HW.NetBook.Samples.Message");

 

// Get the method to call from the type.

MethodInfo ShowMessageMethod = MessageType.GetMethod("ShowMessage");

 

// Create an instance of the Message class.

Object MessageObj = Activator.CreateInstance(MessageType);

 

// Create the arguments array.

Object[] args = new Object[1];

 

// Set the arguments

args[0] = "I'm using late binding!!!";

 

// Invoke the PrintHello method.

ShowMessageMethod.Invoke(MessageObj, args);

In Visual Basic .NET:

' Get the type to use from the assembly.

Dim MessageType As Type = Type.GetType("HW.NetBook.Samples.Message")

 

' Get the method to call from the type.

Dim ShowMessageMethod As MethodInfo = MessageType.GetMethod("ShowMessage")

 

' Create an instance of the Message class.

Dim MessageObj As Object = Activator.CreateInstance(MessageType)

 

' Create the arguments array.

Dim args(0) As Object

 

' Set the arguments

args(0) = "I'm using late binding!!!"

 

' Invoke the PrintHello method.

ShowMessageMethod.Invoke(MessageObj, args)

The first line of code uses the static Type.GetType method to retrieve a Type object that contains information about the HW.NetBook.Samples.Message class. You can use Type.GetType because the Message class is contained in an assembly already loaded.

The second line of code calls the Type object’s GetMethod requesting information on the Message.ShowMessage method. GetMethod returns a MethodInfo object you can use to invoke the ShowMessage method.

The third line of code uses the static Activator.CreateInstance method to create an instance of the Message class. Next, an array of type Object is created to pass a parameter to the Message.ShowMessage method when it is invoked. Even if the method you are calling does not have any parameters, you still need to declare an empty Object array as follows.

In C#:

Object[] args = new Object[1];

And in Visual Basic .NET:

Dim args(0) As Object

Finally, the last line of code invokes the Message.ShowMessage method.

If you need to get type information for a class not in a loaded assembly, you use the static Assembly.Load method. For example, the following code loads the MyOtherAssembly file and retrieves the HW.NetBook.Samples.MyClass type from the assembly.

In C#:

Assembly assem = Assembly.Load("MyOtherAssembly");

// Get the type to use from the assembly.

Type helloType = assem.GetType("HW.NetBook.Samples.MyClass");

In Visual Basic .NET:

Dim assem As [Assembly] = [Assembly].Load("MyOtherAssembly")

' Get the type to use from the assembly.

Dim helloType As Type = assem.GetType("HW.NetBook.Samples.MyClass")

Late binding in Visual Basic .NET

In addition to the methodology shown in the previous section, there’s an easier way to implement late binding in Visual Basic .NET using the Object data type.

VB .NET treats the Object data type in special way. Unlike C#, VB .NET allows you to store an object reference into a variable of type Object and call methods on the object even though the .NET Object class does not implement those methods.

For example, the following code declares a class with a method named “CallMethod” that accepts an Object parameter. In this method there is a single line of code that calls a DisplayMessage method on this object.

Public Class ObjectLateBindingDemo

    Public Sub CallMethod(ByVal obj As Object)

        obj.DisplayMessage()

    End Sub

End Class

In order to get this code to compile, you must set VB .NET’s Option Strict setting to “Off” (for more information on Option Strict, see Chapter 4, “Introduction to Visual Basic .NET”). This tells the compiler to ignore the rules of strict typing.

Turning strict typing off allows you to pass an object reference of any type to CallMethod. The compiler blissfully ignores the fact you declared a variable of type Object and are calling a method (DisplayMessage) not implemented in the Object class.

If you pass an object to CallMethod that implements the DisplayMessage method, everything goes smoothly at run time. However, if you pass an object that does not implement this method, you get a runtime error.

The fact that you have to turn off strict typing in order to use this feature should make you think twice before using it. As I recommended in Chapter 4, “Introduction to Visual Basic .NET”, you should never turn Option Strict off because you miss the benefit of catching errors at compile time rather than run time.

Performance and late binding

Although late binding provides flexibility, a substantial price is paid in performance. This is because the compiler does not know at compile time the class being used, so binding must be performed at runtime instead.

I suggest using late binding only when you absolutely need it. You should stick to early binding as a general rule. If you are designing your applications well, you can achieve a similar effect by means of abstract classes and interfaces as described earlier in this chapter—and without incurring a performance penalty.

Conclusion

This chapter gives a good overview of C# and Visual Basic .NET’s object-oriented features. Again, this is not an exhaustive list of all features, but an introduction to object-orientation in .NET. In the final analysis, both C# and Visual Basic .NET go beyond the OO capabilities of Visual FoxPro and provide a number of advanced features to help you create flexible, well-designed software applications.

 


.NET for Visual FoxPro Developers