Lesson 1: Namespace Fundamentals

Lesson 1: Namespace Fundamentals

Visual Basic .NET and Visual C# provide a full set of object-oriented programming concepts, including abstract classes, interfaces, and overloading or overriding class members. These features are fundamental to the language and are used throughout the Microsoft .NET Framework. By learning how to use these features yourself, you will understand how the .NET Framework is implemented and you ll be better able to use it in your applications.

After this lesson, you will be able to

  • Organize your code using namespaces

  • Create classes and control access to those classes

  • Create abstract and base classes and derive new classes from them

  • Understand how the members of an inherited class can be overloaded, overridden, or shadowed

  • Create interfaces for classes and explain how they are different from abstract classes

Estimated lesson time: 40 minutes

Understanding Namespaces

At the beginning of the code for each Web form, you ll see some generated code that looks like this:

Visual Basic .NET

Imports System Imports System.Web

Visual C#

using System; using System.Web;

These statements permit you to use code from the ASP.NET namespaces System and System.Web without specifying their full names. Without these statements, a call to a simple method from the System namespace, for example, would look like this:

Visual Basic .NET

System.Array.Sort(strArray)

Visual C#

System.Array.Sort(strArray);

By including the System namespace at the beginning of the code, that Array method can be shortened to:

Visual Basic .NET

Array.Sort(strArray)

Visual C#

Array.Sort(strArray);

Namespaces are a way of organizing code. They provide protection from conflicting names, sometimes called namespace collisions. This protection is especially necessary in large projects in which it is very easy for two items to accidentally have the same name. By organizing your code into namespaces, you reduce the chance of these conflicts.To create a namespace, enclose a Class or Module in a Namespace End Namespace block.

To add more than one class or module to a namespace, specify the same namespace for each.

The following code (in boldface) creates a namespace for the Strings module:

Visual Basic .NET

' Project name: MCSDWebAppsVB Namespace Utils Public Module Strings Public Function Sort(ByVal strText As String, _ Optional ByVal bAlphaOrder As Boolean = True) As String ' Declare and initialize a string array. Dim strArray As String() = {""} ' Convert the string to an array using System.String. strArray = strText.Split(" ") ' Use System.Array to sort. System.Array.Sort(strArray) ' If it's not alphabetic order, reverse the array. If Not bAlphaOrder Then ' Use System.Array to reverse. System.Array.Reverse(strArray) End If ' Return the a string. Sort = System.String.Join(" ", strArray) End Function End Module End Namespace

Visual C#

// Project name: MCSDWebAppsCS namespace Utils { class Strings { // Takes a string, sorts it, and returns a string. public static string Sort(string strText , bool bAlphaOrder) { // Declare and initialize a string array. string[] strArray = {""}; char[] strSep = {' '}; // Convert the string to an array using System.String. strArray = strText.Split(strSep); // Use System.Array to sort. System.Array.Sort(strArray); // If it's not alphabetic order, reverse the array. if (!bAlphaOrder) { // Use System.Array to reverse. System.Array.Reverse(strArray); } // Return the string. return System.String.Join(" ", strArray); } // Same method with one parameter. public static string Sort(string strText) { return Sort(strText, true); } } }

IMPORTANT
The preceding example also uses two .NET namespaces: System.Array and System.String. The System namespaces are the new way to access functions you used to access in the Microsoft Windows API through Declare statements. This is even easier now because Visual Basic .NET, Visual C#, and the .NET Framework use the same fundamental types and default parameter passing conventions. Unlike the Windows API, the System namespaces provide an object-oriented interface to the Windows functions. This might take some getting used to if you are already familiar with the Windows API. However, the benefits of the common language runtime and managed code are enormous.

Because namespaces are an organization tool for code you want to use elsewhere, they are public by definition. When used in code, the reference takes the form:

ProjectName.NameSpace.ModuleName.MemberName

You can use code from a namespace in your application in one of two ways:

  • Use the fully qualified name of the member. The following code (in boldface) calls the Sort function from the MCSDWebAppsVB or MCSDWebAppsCS project s Utils namespace:

    Visual Basic .NET

    Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ' Sort the text.  txtValue.Text = MCSDWebAppsVB.Utils.Strings.Sort(txtValue.Text) End Sub End Class

    Visual C#

    private void Button1_Click(object sender, System.EventArgs e ) {  txtValue.Text = MCSDWebAppsCS.Utils.Strings.Sort(txtValue.Text); }

  • Add a Visual Basic .NET Imports or Visual C# using statement at the beginning of a class or module. The Imports or using statement provides a shortcut to the member name you no longer have to use the full name in code. The following code (in boldface) uses the namespace in the MCSDWebAppsVB or MCSDWebAppsCS project s Utils namespace:

    Visual Basic .NET

    ' Import the Utils namespace from the MCSDWebAppsVB project. Imports MCSDWebAppsVB.Utils Public Class Namespaces ' Declarations and initialization code omitted Private Sub butSort_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ' Call a helper function  txtValue.Text = Strings.Sort(txtValue.Text) End Sub End Class

    Visual C#

    // Import the Utils namespace from the MCSDWebAppsCS project. using MCSDWebAppsCS.Utils; namespace csNamespaces { public class Namespaces : System.Web.UI.Page { // Declarations and initialization code omitted... private void butSort_Click(object sender, EventArgs e) {  txtValue.Text = Strings.Sort(txtValue.Text); } } }

Namespaces use dot notation (.) to specify hierarchy. Consider the following namespace declaration:

Visual Basic .NET

' Project name: MCSDWebAppsVB Namespace Utils Namespace Types Class Digits Public Enum Numeric Zero One Two Three Four Five Six Seven Eight Nine End Enum End Class End Namespace End Namespace

Visual C#

// Project name: MCSDWebAppsCS namespace Utils { namespace Types { class Digits { public enum Numeric { Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine } } } }

This declaration is equivalent to the following code:

Visual Basic .NET

' Project name: MCSDWebAppsVB Namespace Utils.Types Class Digits Public Enum Numeric Zero One Two Three Four Five Six Seven Eight Nine End Enum End Class End Namespace

Visual C#

// Project name: MCSDWebAppsCS namespace Utils.Types { class Digits { public enum Numeric { Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine } } }

To use either of the preceding namespace declarations, you can use the fully qualified name, as shown here:

Visual Basic .NET

Dim numVar As MCSDWebAppsVB.Utils.Types.Digits.Numeric

Visual C#

MCSDWebAppsCS.Utils.Types.Digits.Numeric numVar;

Or you can use an Imports or using statement, as shown here:

Visual Basic .NET

Imports MCSDWebAppsVB.Utils.Types Dim numVar As Digits.Numeric

Visual C#

using MCSDWebAppsCS.Utils.Types; Digits.Numeric numVar;

References vs. Imports

You add project references to use namespaces outside of the current project. Use the Imports statement to provide a shortcut to that namespace. The Imports statement simply provides an abbreviated way to refer to a namespace in code.

To add a reference to a project:

  1. From the Project menu, choose Add Reference. Visual Studio .NET displays the Add Reference dialog box, as shown in Figure 3-1.

    figure 3-1 adding a project reference

    Figure 3-1. Adding a project reference

  2. Select the reference to add from the within the .NET, COM, or Project component lists. Click OK.

Visual Studio adds references to the project in displayed Solution Explorer.

Classes and Modules

Visual Studio projects store code in classes and modules. Earlier versions of Visual Basic introduced the concept of classes and how classes are unique from code modules. This concept is the same in Visual Basic .NET: you use classes for items that define their own storage, and you use modules for code that does not have persistent data. Also, you instantiate classes to create objects before you use them, whereas you can simply call code modules directly.

In Visual C#, however, all code is contained in a class. If you want to create methods or properties that can be called without first creating an object, declare those items as static. The Utils class s Sort method created in the preceding section demonstrates the Visual C# equivalent of a Visual Basic .NET module.

Visual Basic .NET and Visual C# use six key concepts for working with modules and classes. These concepts are new to Visual Basic and, of course, Visual C#, because that is an entirely new language. However, C++ programmers should already be familiar with these concepts. Table 3-1 describes these key concepts in both languages.

Table 3-1. Key Object-Oriented Concepts

Concept

In Visual Basic .NET

In Visual C#

Definition

You define whether something is a class or a module by using Class End Class or Module End Module blocks. In earlier versions, this was implicit with the file type and you could have only one Class or Module per file.

You define classes using the class keyword. All executable code is part of a class.

Access

There are five levels of access to classes, modules, and their members: Public, Protected, Friend, Protected Friend, and Private. Access is also explicit in the item s definition, rather than hidden in file properties.

There are five levels of access to classes and their members: public, protected, internal, protected internal, and private.

Inheritance

Classes can inherit members from each other and override, shadow, or overload members of the inherited class.

Classes can inherit members from base classes and override or overload members of the inherited class.

Constructors and destructors

Classes have New and Finalize methods that are called when an object based on the class is created or destroyed.

Classes have constructors and destructors that are called when an object based on the class is created or destroyed. Constructor methods have the same name as their class, and destructor methods use the class name preceded by a tilde (~).

Delegates

The Delegates statement provides a safe way to call methods by their address rather than by their name. This is the .NET equivalent of a callback. Delegates are commonly used with events and asynchronous procedures.

The delegates keyword provides a safe way to call methods by their address rather than by their name. This is the .NET equivalent of a callback. Delegates are commonly used with events and asynchronous procedures.

Abstract classes and interfaces

You can create interfaces and abstract classes. Interfaces define the member names and member parameter lists for classes that use the interface. Abstract classes provide the members to be inherited by classes derived from them.

You can create interfaces and abstract classes. Interfaces define the member names and member parameter lists for classes that use the interface. Abstract classes provide the members to be inherited by classes derived from them.

Creating Classes/Modules and Providing Access

In Visual Basic .NET, use Class End Class and Module End Module blocks to define classes and modules. In Visual C#, use the class keyword to define classes. You can have one or more classes and/or modules per file. Use one of the access keywords described in Table 3-2 to define which other classes and modules can use the members of the current class or module.

Table 3-2. Levels of Access for Classes and Modules

Visual Basic

Visual C#

Available to

Public

public

All members in all classes and projects.

Friend

internal

All members in the current project.

Protected

protected

All members in the current class and in classes derived from this member s class. Can be used only in member definitions, not for class or module definitions.

Protected Friend

protected internal

All members in the current project and all members in classes derived from this member s class. Can be used only in member definitions, not for class or module definitions.

Private

private

Members of the current class only.

For example, the following class is available only in the current project:

Visual Basic .NET

Friend Class Translator Private mstrText As String ' Controls access to the class-level ' variables. Public Property Text() As String Get Text = mstrText End Get Set(ByVal Value As String) mstrText = Value End Set End Property ' Translates the value in the Text property. Public Sub Translate() Dim strWord As String, intCount As Integer Dim arrWords() As String Dim bCaps As Boolean ' Convert the string to an array using System.String. arrWords = mstrText.Split(" ") For intCount = 0 To UBound(arrWords) ' Check if word is capitalized. If LCase(arrWords(intCount)) <> arrWords(intCount) Then bCaps = True arrWords(intCount) = LCase(arrWords(intCount)) End If strWord = arrWords(intCount) ' Do translation. If strWord <> "" Then strWord = Right(strWord, Len(strWord) - 1) & _ Left(strWord, 1) & "ay" ' Recapitalize if necessary If bCaps Then strWord = UCase(Left(strWord, 1)) & _ Right(strWord, Len(strWord) - 1) End If End If ' Store back in the array. arrWords(intCount) = strWord ' Reset caps flag. bCaps = False Next ' Rebuild string from array. mstrText = String.Join(" ", arrWords) End Sub End Class

Visual C#

internal class Translator { string mstrText; // Controls access to class-level variables. public string Text { get { return mstrText; } set { mstrText = value; } } // Translates the value in the Text property. public void Translate() { string strWord; string[] arrWords; bool bCaps = false; // Convert the string into an array using System.String. arrWords = mstrText.Split(' '); for(int intCount = 0; intCount <= arrWords.GetUpperBound(0); intCount++) { // Change to lowercase. strWord = arrWords[intCount].ToLower(); // Check if word is capitalized. if(!arrWords[intCount].Equals(strWord)) bCaps = true; // Do translation. if(strWord != "") { strWord = strWord.Substring(1,strWord.Length - 1) + strWord.Substring(0,1) + "ay"; // Recapitalize if necessary. if(bCaps) strWord = strWord.Substring(0,1).ToUpper() + strWord.Substring(1, strWord.Length - 1); } // Store the word back in the array. arrWords[intCount] = strWord; // Reset the caps flag. bCaps = false; } // Rebuild the string from the array. mstrText = String.Join(" ", arrWords); } }

You can access the Text property and Translate method of the preceding Translator class from within the project where it is included, but not from other projects. The following code (in boldface) shows using the Text and Translate members from a Web form:

Visual Basic .NET

Private Sub butTranslate_Click(ByVal sender As System.Object , _ ByVal e As System.EventArgs) Handles butTranslate.Click ' Create a new Translator object.  Dim TranslateText As New Translator() ' Put the source text into the translator.  TranslateText.Text = txtInput.Text ' Turn the crank.  TranslateText.Translate() ' Display result.  txtOutput.Text = TranslateText.Text End Sub

Visual C#

private void butTranslate_Click(object sender, System.EventArgs e) { // Create a new Translator object.  Translator TranslateText = new Translator(); // Put the source text into translator.  TranslateText.Text = txtInput.Text; // Turn the crank.  TranslateText.Translate(); // Display the result.  txtOutput.Text = TranslateText.Text; }

When you look at the code Visual Studio generates for a Web form, notice that the server controls on the Web form are declared as Protected:

Visual Basic .NET

Protected System.Web.UI.WebControls.TextBox txtInput Protected System.Web.UI.WebControls.Button butTranslate

Visual C#

protected System.Web.UI.WebControls.TextBox txtInput; protected System.Web.UI.WebControls.Button butTranslate;

By default, server control objects are set to be available only in the current Web form and in Web forms derived from that Web form.

Inheritance: Who s Deriving?

Inheritance is the process of basing one class on another. In this process, a base class provides methods, properties, and other members to a derived class. The advantage of inheritance is that you can write and maintain code once it is in the base class and reuse it over and over in the derived classes. How familiar you are with inheritance usually depends on which programming language you have been using.

  • Visual Basic .NET introduces inheritance to Visual Basic programming. This is a huge step forward for the language and is the reason that many things had to change between all earlier versions of Visual Basic. Windows Forms and data types are now derived from the .NET Framework, rather than being implemented through the Ruby engine that powered earlier versions of Visual Basic. You ll now find a lot more agreement and consistency between Visual Basic and Windows.

  • C# programmers who are familiar with C++ are probably very familiar with the concept of inheritance. However, Visual C# adds refinements not found in C++ syntax, including the interface and abstract keywords.

How you specify inheritance also depends on your programming language. In Visual Basic .NET, you use the Inherits statement to base a new class on an existing one. In Visual C#, no keyword is needed; instead, you use the class definition. The following lines show the different syntaxes for each language:

Visual Basic .NET

Public Class DerivedClass Inherits BaseClass End Class

Visual C#

public class DerivedClass : BaseClass { }

Visual Studio uses the keywords described in Table 3-3 for creating base classes and deriving new classes from them. The sections that follow explain how to use each of these keywords.

Table 3-3. Overview of the Inheritance Keywords

Visual Basic

Visual C#

Use to

Inherits

derivedclass : baseclass

Base one class on another, inheriting members from the base class.

Overridable

virtual

Declare that a member of the base class can be overridden in a derived class.

Overrides

override

Declare that a member of a derived class overrides the member of the same name in the base class.

Shadows

new

Declare that a member of a derived class hides the member of the same name in the base class.

MustInherit

abstract

Declare that a class provides a template for derived classes. This type of class is called an abstract class, and it can t be instantiated.

MustOverride

abstract

Declare that a member of a class provides a template for derived members. This type of member is called an abstract member, and it can t be invoked.

MyBase

base

Call a base class member from within the derived class.

Me

this

Call a member of the current instance of a class.

Interface

interface

Create an interface that defines the members a class must provide.

Implements

classname : interfacename

Use an interface definition in a class.

Before we look at inheritance in more detail, you should understand the two things you can t do with inheritance:

  • You can t inherit from more than one base class in a single derived class definition. The concept of multiple inheritance exists in many object-oriented programming languages, but as a practical matter, it is not widely used.

  • Derived Web forms inherit the code from their base Web form, but not the base form s HTML or server controls. That is because the Web form s class (.vb or .cs) is separate from its appearance (.aspx).

Overriding, Overloading, and Shadowing Members

A derived class inherits the members of its base class. If the derived class defines a member with the same signature, the derived member overrides the base member. A member s signature includes its name, parameter list, parameter types, and return type.

If a derived class defines a member with the same name but a different parameter list, parameter type, or return type than the base member, the derived member either overloads or shadows the base member. A member overloads another member if the base member is still available. A member shadows another member if the derived member replaces the base member.

In the following example, the class Sphere is based on the class Circle. In Visual Basic .NET, members that can be overridden must be declared as Overridable (1). In Visual C#, members that can be overridden must be declared as virtual (1). The Circle class is shown here:

Visual Basic .NET

Public Class Circle Private sxCenter, syCenter As Single Private sRadius As Single Public Property Top() As Single Get Top = sxCenter - sRadius End Get Set(ByVal Value As Single) sxCenter = Value + sRadius End Set End Property Public Property Left() As Single Get Left = syCenter - sRadius End Get Set(ByVal Value As Single) syCenter = Value + sRadius End Set End Property Public Overridable Function Area() As Single ' (1) Area = System.Math.PI * (sRadius ^ 2) End Function Public Function Perimeter() As Single Perimeter = 2 * sRadius * System.Math.PI End Function Public Property Radius() As Single Get Radius = sRadius End Get Set(ByVal Value As Single) sRadius = Value End Set End Property Public Overridable Sub Center(ByVal X As Single, _ ByVal Y As Single) ' (1) sxCenter = X syCenter = Y End Sub End Class

Visual C#

public class Circle { float fxCenter, fyCenter, fRadius; // Constructor public Circle() { // Initialize internal variables. fxCenter = 0; fyCenter = 0; fRadius = 0; } public float Top { get { return fxCenter - fRadius; } set { fxCenter = value + fRadius; } } public float Left { get { return fyCenter - fRadius; } set { fyCenter = value + fRadius; } } public virtual float Area() // (1) { return (float)(System.Math.PI * Math.Pow((double)fRadius, 2)); } public float Perimeter() { return 2 * fRadius * (float)System.Math.PI; } public float Radius { get { return fRadius; } set { fRadius = value; } } public virtual void Center(float X, float Y) // (1) { fxCenter = X; fyCenter = Y; } }

In the following example, Sphere class inherits all of the methods and properties defined in Circle (1). Sphere overrides the method for Area (2) because spheres use a different formula for this calculation. Sphere shadows the Center method (3) because spheres have an additional coordinate (z) and you wouldn t want users to accidentally set xy-coordinates without setting a z-coordinate. Notice that Sphere uses the Visual Basic .NET MyBase or the Visual C# base keyword (4) to call the base class s Center method within the shadowed method.

Visual Basic .NET

Public Class Sphere Inherits Circle ' (1) Private sCenter As Single Public Overrides Function Area() As Single ' (2) Area = 4 * System.Math.PI * (MyBase.Radius ^ 2) End Function Public Shadows Sub Center(ByVal X As Single, _ ByVal Y As Single, ByVal Z As Single) ' (3) MyBase.Center(X, Y) ' (4) sCenter = Z End Sub Public Function Volume() As Single Volume = (4 / 3) * System.Math.PI * (Radius ^ 3) End Function Public Property Front() As Single Get Front = sCenter - Radius End Get Set(ByVal Value As Single) sCenter = Value + MyBase.Radius End Set End Property End Class

Visual C#

public class Sphere : Circle // (1) { float fCenter; // Constructor. public Sphere() { // Initialize internal variable. fCenter = 0; } public override float Area() // (2) { return (float)(4 * Math.PI * Math.Pow((double)base.Radius, 2)); } public new void Center(float X, float Y) // (3) { this.Center(X, Y, 0); } public void Center(float X, float Y, float Z) { base.Center(X, Y); // (4) fCenter = Z; } public float Volume() { return (float)((4 / 3) * System.Math.PI * Math.Pow((double)base.Radius, 3)); } public float Front { get { return fCenter - base.Radius; } set { fCenter = value + base.Radius; } } }

To see how inheritance works, create a new object of each type and call each object s properties and methods. The following code demonstrates the Circle and Sphere classes by displaying calculated values on a Web form:

Visual Basic .NET

Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim MyCircle As New Circle() MyCircle.Radius = 2 MyCircle.Center(10, 2) Response.Write("Circle area: " & MyCircle.Area & "<br>") Response.Write("Circle circumference: " & MyCircle.Perimeter _ & "<br>") Dim MySphere As New Sphere() MySphere.Radius = 10 MySphere.Center(10, 20, 25) Response.Write("Sphere top: " & MySphere.Top & "<br>") Response.Write("Sphere left: " & MySphere.Left & "<br>") Response.Write("Sphere front: " & MySphere.Front & "<br> ") Response.Write("Sphere volume: " & MySphere.Volume & "<b r>") Response.Write("Sphere surface area: " & MySphere.Area & _  "<br>") Response.Write("Sphere circumference: " & _ MySphere.Perimeter & "<br>") End Sub

Visual C#

private void Page_Load(object sender, System.EventArgs e) { Circle MyCircle = new Circle(); MyCircle.Radius = 2; MyCircle.Center(10, 2); Response.Write("Circle area: " + MyCircle.Area() + " <br>"); Response.Write("Circle circumference: " + MyCircle.Perimeter() +  "<br>"); Sphere MySphere = new Sphere(); MySphere.Radius = 10; MySphere.Center(10,20,25); Response.Write("Sphere top: " + MySphere.Top + "<br> "); Response.Write("Sphere left: " + MySphere.Left + "<b r>"); Response.Write("Sphere front: " + MySphere.Front + " <br>"); Response.Write("Sphere volume: " + MySphere.Volume() + "<br>"); Response.Write("Sphere surface area: " + MySphere.Area() + "<br>"); Response.Write("Sphere circumference: " + MySphere.Perimeter() +  "<br>"); }

In the Abstract

Visual Studio also lets you define abstract classes. An abstract class is a class that defines an interface for derived classes. An abstract class is essentially a contract saying that all classes based on it will provide certain methods and properties. You can t create objects from abstract classes you can only derive new classes from them.

Abstract classes are declared with the Visual Basic .NET MustInherit or the Visual C# abstract keyword. Methods and properties that base classes must provide are declared as MustOverride in Visual Basic .NET or as abstract in Visual C#. The following Shape class demonstrates an abstract class:

Visual Basic .NET

' Definition of abstract class. Public MustInherit Class Shape Public MustOverride Property Top() As Single Public MustOverride Property Left() As Single Public MustOverride Function Area() As Single Public MustOverride Function Perimeter() As Single End Class

Visual C#

// Definition of abstract class. public abstract class Shape { public Shape() { } public abstract float Top { get; set; } public abstract float Left { get; set; } public abstract float Area(); public abstract float Perimeter(); }

Notice that the Visual Basic .NET MustOverride and Visual C# abstract members are just definitions there is no procedure body statement because these members will be defined (overridden) in the derived class.

The following Circle class demonstrates how an abstract class is inherited. In Visual Basic .NET, the Inherits statement (1) declares that this class is based on the abstract class Shape. The Overrides keywords (2) are required in each of the member definitions that override members of the abstract class. In Visual C#, the class definition (1) declares that this class is based on the abstract class Shape. The override keywords (2) are required in each of the member definitions that override members of the abstract class.

Visual Basic .NET

Public Class Circle Inherits Shape ' (1) Private sxCenter, syCenter As Single Private sRadius As Single Public Overrides Property Top() As Single ' (2) Get Top = sxCenter - sRadius End Get Set(ByVal Value As Single) sxCenter = Value + sRadius End Set End Property Public Overrides Property Left() As Single Get Left = syCenter - sRadius End Get Set(ByVal Value As Single) syCenter = Value + sRadius End Set End Property Public Overrides Function Area() As Single Area = 2 * System.Math.PI * (sRadius ^ 2) End Function Public Overrides Function Perimeter() As Single Perimeter = 2 * sRadius * System.Math.PI End Function Public Property Radius() As Single Get Radius = sRadius End Get Set(ByVal Value As Single) sRadius = Value End Set End Property Public Overridable Sub Center(ByVal X As Single, ByVal Y As Single) sxCenter = X syCenter = Y End Sub End Class

Visual C#

public class Circle : Shape // (1) { float fxCenter, fyCenter, fRadius; // Constructor. public Circle() { // Initialize internal variables. fxCenter = 0; fyCenter = 0; fRadius = 0; } public override float Top // (2) { get { return fxCenter - fRadius; } set { fxCenter = value + fRadius; } } public override float Left { get { return fyCenter - fRadius; } set { fyCenter = value + fRadius; } } public override float Area() { return (float)(2 * System.Math.PI * Math.Pow((double)fRadius, 2)); } public override float Perimeter() { return 2 * fRadius * (float)System.Math.PI; } public float Radius { get { return fRadius; } set { fRadius = value; } } public virtual void Center(float X, float Y) { fxCenter = X; fyCenter = Y; } }

Delegates and Events

Delegates are types used to invoke one or more methods where the actual method invoked is determined at run time. This provides a safe way for derived objects to subscribe to events provided by their base class. Delegates also provide a way for programs to respond to asynchronous procedures.

Simply put, delegates provide a way to invoke methods by their address rather than by their name. For example, the following Web form code declares a delegate named MathDelegate and then invokes two different methods through the delegate from the AboutNumber procedure. When run, this code displays the message The number 42 is even and is not prime.

Visual Basic

Public Class SimpleDelegate Inherits System.Web.UI.Page ' Declare a delegate. Delegate Function MathDelegate(ByVal x As Integer) As Boolean Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim MyNumber As Integer = 42 Response.Write("<h2>Using Delegates</h2><hr />") Response.Write("The number " & MyNumber.ToString()) ' Call AboutNumber to invoke IsEven. AboutNumber(AddressOf IsEven, MyNumber) Response.Write(" even and it ") ' Call AboutNumber to invoke IsPrime. AboutNumber(AddressOf IsPrime, MyNumber) Response.Write(" prime.") End Sub ' Invoke the delegate. Sub AboutNumber(ByVal Func As MathDelegate, ByVal x As Integer) If Func.Invoke(x) Then Response.Write(" is ") Else Response.Write(" is not ") End If End Sub ' These procedures may be invoked through MathDelegate. Function IsEven(ByVal x As Integer) As Boolean If x Mod 2 Then Return False Else Return True End If End Function Function IsPrime(ByVal x As Integer) As Boolean Dim i As Integer For i = 2 To x \ 2 If (x Mod i = 0) Then Return False End If Next Return True End Function End Class

Visual C#

public class SimpleDelegate : System.Web.UI.Page { delegate bool MathDelegate(int x); private void Page_Load(object sender, System.EventArgs e) { int MyNumber = 42; Response.Write("<h2>Using Delegates</h2><hr />"); Response.Write("The number " + MyNumber.ToString()); // Call AboutNumber to invoke IsEven. AboutNumber(new MathDelegate(IsEven), MyNumber); Response.Write(" even and it "); // Call AboutNumber to invoke IsPrime. AboutNumber(new MathDelegate(IsPrime), MyNumber); Response.Write(" prime."); } // Invoke the delegate. void AboutNumber(MathDelegate Func, int x) { if (Func(x)) Response.Write(" is "); else Response.Write(" is not "); } // These procedures may be invoked through MathDelegate. bool IsEven(int x ) { if (x % 2 == 0) return true; else return false; } bool IsPrime(int x ) { for(int i = 2; i > (x /2); i++) if (x % i == 0) return true; return false; } }

NOTE
The delegate s declaration must match the signature of the methods invoked. This rule ensures that delegation is type-safe.

But why provide an alternative way to invoke methods? Perhaps the most important reason is that delegates provide the flexibility required for responding to events and asynchronous tasks running in separate threads.

For example, the following class finds factors of a given seed number. Because such calculations may take a while, the class launches the calculations in a separate thread. The class declares an event and raises the event when a factor is found, returning the factor in the event arguments. The delegate that appears above the event declaration allows Web forms (or other code) to subscribe to the event.

Visual Basic

' MathClass exposes an event that fires every time a new factor is found. Public Class MathClass ' Declares the signature of the procedures that handle this event. Delegate Sub OnFactorHandler(ByVal sender As Object, _ ByVal e As MathEventArgs) ' Exposes the event. Public Event OnGotFactor As OnFactorHandler ' Internal variable used by this class. Protected Seed As Double ' This class uses a delegate from the System.Threading namespace to ' invoke the CalcFactors procedure in asynchronously in as separate ' thread. Protected Thread As New System.Threading.Thread(AddressOf CalcFactors) ' Class constructor sets the seed value and launches the thread. Public Sub New(ByVal X As Integer) Seed = X ' Launch the thread to begin calculating factors. Thread.Start() End Sub ' Provides a way to stop the asynchronous processing. Public Sub StopCalc() Thread.Abort() End Sub ' Finds the positive factors of the seed value and raises an event each ' time one is found. Protected Sub CalcFactors() ' Create a MathEventArgs object as a means to return the factors. Dim e As New MathEventArgs ' Declare a counter. Dim i As Integer For i = 1 To Math.Abs(Seed \ 2) ' If the number is evenly divisible by the counter, return the ' counter in the event arguments. If (Seed Mod i = 0) Then e.Factor = i RaiseEvent OnGotFactor(Me, e) End If Next End Sub End Class ' This class provides a way to return values from the event. Public Class MathEventArgs Inherits System.EventArgs Public Factor As Integer End Class

Visual C#

// MathClass exposes an event that fires every time a new factor is found. public class MathClass { // Declares the signature of the procedures that handle this event. public delegate void OnFactorHandler(Object sender, MathEventArgs e); // Exposes the event. public event OnFactorHandler OnGotFactor; // Internal variable used by this class. protected long Seed; // This class uses a delegate from the System.Threading namespace to // invoke the CalcFactors procedure in asynchronously in as // separate thread. System.Threading.Thread CalcThread; // Class constructor sets the seed value and launches the thread. public MathClass(int X) { Seed = X; // Create a new thread for the calculations. CalcThread = new System.Threading.Thread(new System.Threading.ThreadStart(CalcFactors)); // Launch the thread to begin calculating factors. CalcThread.Start(); } // Provides a way to stop the asynchronous processing. public void StopCalc() { CalcThread.Abort(); } // Finds the positive factors of the seed value and raises an event each // time one is found. public void CalcFactors() { // Create a MathEventArgs object as a means to return the factors. MathEventArgs e = new MathEventArgs(); // Declare a counter. for (int i = 1; i < Math.Abs(Seed / 2); i++) { // If there is a listener for the OnGotFactor event // and if the number is evenly divisible by the // counter, return the counter in the event // arguments. if ((OnGotFactor != null) && (Seed % i == 0)) { e.Factor = i; // Raise the event. OnGotFactor(this, e); } } } } // This class provides a way to return values from the event. public class MathEventArgs : System.EventArgs { public int Factor; }

To subscribe to the event, a Web form can declare an instance of the class using the WithEvents keyword in Visual Basic or using the event += new delegate (handler) event handler syntax in Visual C#, as shown in the following Web form code. Notice that both the ShowFactor and StopCalc event handler procedures respond to the MathClass class s OnGotFactor event that s a key part of the flexibility afforded by delegates.

Visual Basic

Public Class EventDelegate Inherits System.Web.UI.Page ' Create an object and subscribe to it's events. WithEvents MyMath As MathClass Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then ' Create a new instance of MathClass with a very large ' seed value. MyMath = New MathClass(Integer.MaxValue - 1) ' Store the class in a Session variable Session("MyMath") = MyMath Else ' Get the stored Session variable MyMath = Session("MyMath") End If End Sub ' This procedure subscribes to the MathClass OnGotFactor event. Sub ShowFactor(ByVal sender As Object, ByVal e As MathEventArgs) _ Handles MyMath.OnGotFactor ' Save Factor in Session object for retrieval in ' PreRender page event. Session("Factor") = e.Factor End Sub ' This procedure also subscribes to the MathClass OnGotFactor event. Sub StopCalc(ByVal sender As Object, ByVal e As MathEventArgs) _ Handles MyMath.OnGotFactor ' After the limit, stop the calculations. If e.Factor > CType(txtLimit.Text, Integer) Then ' Stop the MathClass thread. MyMath.StopCalc() End If End Sub ' This procedure handles the Page's PreRender event. ' It's required to display the asynchronously calculated Factor. Private Sub Page_PreRender(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.PreRender ' Get the Factor that was calculated asynchronously. If Not IsNothing(Session("Factor")) Then lblFactor.Text = Session("Factor") End If End Sub End Class

Visual C#

public class EventDelegate : System.Web.UI.Page { protected System.Web.UI.WebControls.Label lblFactor; protected System.Web.UI.WebControls.TextBox txtLimit; protected System.Web.UI.WebControls.RangeValidator RangeValidator1; protected System.Web.UI.WebControls.Button butUpdate; // Declare an object. MathClass MyMath; private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { // Create a new instance of MathClass with a // very large seed value. MyMath = new MathClass(Int32.MaxValue - 1); // Store the class in a Session variable Session["MyMath"] = MyMath; } else // Get the stored Session variable MyMath = (MathClass)Session["MyMath"]; // Subscribe to the OnGotFactor event. MyMath.OnGotFactor += new MathClass.OnFactorHandler(this.ShowFactor); MyMath.OnGotFactor += new MathClass.OnFactorHandler(this.StopCalc); } // This procedure subscribes to the MathClass OnGotFactor event. private void ShowFactor(Object sender, MathEventArgs e ) { // Save the factor as the event is raised. Session["Factor"] = e.Factor.ToString(); } // This procedure also subscribes to the MathClass OnGotFactor event. private void StopCalc(Object sender, MathEventArgs e ) { // After the limit, stop the calculations. if (e.Factor > Convert.ToInt32(txtLimit.Text)) // Stop the MathClass thread. MyMath.StopCalc(); } // This procedure handles the Page's PreRender event. // It's required to display the asynchronously calculated Factor. private void Page_PreRender(object sender, EventArgs e) { // Display the current Factor. if (Session["Factor"] != null) lblFactor.Text = Session["Factor"].ToString(); } }

At run time, the preceding Web form code displays the largest factor found by the time the Web form has finished processing. Clicking the Update button refreshes that number from the MathClass object as it calculates factors in a separate thread.

Visual Basic .NET provides two ways to use a delegate. The first is shown in the preceding example using the WithEvents and Handles keywords. The second way uses the Visual Basic .NET AddHandler and RemoveHandler statements, and it looks a lot more like C# syntax. For example, the following code associates the ShowFactor and StopCalc methods with the OnGotFactor event:

Visual Basic

AddHandler MyMath.OnGotFactor, AddressOf this.ShowFactor AddHandler MyMath.OnGotFactor, AddressOf this.StopCalc

The advantage of this syntax is that you can also disassociate a method from the event using the RemoveHandler statement.

Interface-to-Face

Interfaces are similar to abstract classes in that they both provide a template that you can use to create new classes. The difference is that interfaces don t provide any implementation of class members, whereas abstract classes can implement members that then become common to all the classes derived from them.

When you implement a particular interface in a class, instances of that class can be used for any argument or variable declared as that interface. For example, the following code declares an interface for the shape objects created earlier in this lesson:

Visual Basic .NET

' Interface for all shapes. Public Interface IFigure Property Top() As Single Property Left() As Single Function Area() As Single Function Perimeter() As Single End Interface

Visual C#

// Interface for all shapes. public interface IFigure { float Top { get; set; } float Left { get; set; } float Area(); float Perimeter(); }

To use the interface, implement it in a class, as shown here in boldface:

Visual Basic .NET

' Definition of abstract class. Public MustInherit Class Shape  Implements IFigure Public MustOverride Property Top() As Single Implements IFigure.Top Public MustOverride Property Left() As Single Implements IFigure.Left Public MustOverride Function Area() As Single Implements IFigure.Area Public MustOverride Function Perimeter() As Single Implements _ IFigure.Perimeter End Class

Visual C#

public abstract class Shape : IFigure { // Constructor public Shape() { } public abstract float Top { get; set; } public abstract float Left { get; set; } public abstract float Area(); public abstract float Perimeter(); }

Because the Shape abstract class in this example implements the IFigure interface, all classes derived from Shape inherit the implementation of IFigure as well. This means that objects of the type Circle and Sphere, which derive from Shape, can be used as the arguments of the type IFigure, as shown in the following procedure calls:

Visual Basic .NET

Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Show header. Response.Write("<h2>Using Inheritance</h2><hr />") ' Create a circle. Dim MyCircle As New Circle() MyCircle.Radius = 2 MyCircle.Center(10, 2) ' Create a sphere. Dim MySphere As New Sphere() MySphere.Radius = 10 MySphere.Center(10, 20, 25) ' Show info about the shapes. ShowShapeInfo(MySphere) ShowShapeInfo(MyCircle) End Sub ' Display the shape info on the Web form. Sub ShowShapeInfo(ByVal Shape As IFigure) Response.Write("Shape top: " & Shape.Top & "<br>") Response.Write("Shape left: " & Shape.Left & "<br>") Response.Write("Shape perimeter: " & Shape.Perimeter & "<br>") Response.Write("Shape surface area: " & Shape.Area & _  "<br>") Response.Write("Shape perimeter: " & _ Shape.Perimeter & "<br>") End Sub

Visual C#

private void Page_Load(object sender, System.EventArgs e) { // Show header. Response.Write("<h2>Using Inheritance</h2><hr />"); // Create a circle. Circle MyCircle = new Circle(); MyCircle.Radius = 2; MyCircle.Center(10, 2); // Create a sphere. Sphere MySphere = new Sphere(); MySphere.Radius = 10; MySphere.Center(10,20,25); // Show info about each shape. ShowShapeInfo(MySphere); ShowShapeInfo(MyCircle); } // Display the shape info on the Web form. private void ShowShapeInfo(IFigure Shape) { // Since Shape argument is IFigure, we know it has these membe rs. Response.Write(String.Format("Shape top: {0} <br>", Shape.Top)); Response.Write(String.Format("Shape left: {0} <br>", Shape.Left)); Response.Write(String.Format("Shape perimeter: {0} <br>", Shape.Perimeter())); Response.Write(String.Format("Shape area: {0} <br>", Shape.Area())); }

The key here is that all items defined in the interface must exist in any class that implements the interface. If you omit any member, you get an error when you compile the code.



MCAD(s)MCSD Self-Paced Training Kit(c) Developing Web Applications With Microsoft Visual Basic. Net and Microsoft V[.  .. ]0-315
MCAD(s)MCSD Self-Paced Training Kit(c) Developing Web Applications With Microsoft Visual Basic. Net and Microsoft V[. .. ]0-315
ISBN: N/A
EAN: N/A
Year: 2003
Pages: 118

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net