generics parameter; MyType bees fixed to a certain type。 So say you declared MyType as follows: Dim cls As MyType = New MyType(Of Integer) () Now any references to GenericType within MyType will bee Integer。 This form of generics solves many problems; as illustrated in Chapter 9。 But in the case of the IWorkbook; we don’t want a fixed type。 We want the ability to have a collection type contain mixed types of IWorksheet。 The way to achieve that is to use generics methods; like this: Function Method(Of GenericType)() As MyType(Of GenericType) End Function Now the generics parameter is associated with the method; rather than the type。 And that means MyType can mix types。 So we could have different IWorksheet types in a single work book。 And wouldn’t it be great if there were a default property with mixed types? But you can’t have generics default properties and properties that are not declared at the type level。 Thus; the use of a generics parameter with a default property or property will not work。 In my opinion; that is a real design flaw in Visual Basic; because it means we need to write code like this: …………………………………………………………Page 323…………………………………………………………… CH AP T E R 1 1 ■ L E A R N IN G AB O U T 。 N E T G E N E R I CS 301 Imports Devspace。Trader。mon Imports System。Reflection _ Public Interface IWorkbook Inherits IDebug Function GetSheet(Of BaseType)(ByVal identifier As String) _ As IWorksheet(Of BaseType) ReadOnly Property Identifier() As String Default Property Item(ByVal identifier As String) As IWorksheetBase End Interface In this modified declaration; the method GetSheet() acts like the Get part of the default property; but notice where the generics parameter BaseType is declared。 The declaration is after the method identifier and before the first bracket。 In the case of IWorkbook; we use the method…level generics parameter to allow the caller to determine the type of the work sheet instances。 The implementation of IWorkbook has to do nothing other than perform the appropriate cast。 Method…level generics parameters are great when you are dealing with mixed types; as in the case of IWorkbook。 The code to retrieve a worksheet that previously needed a cast is rewritten as follows: Dim workbook As IWorkbook Dim worksheet As IWorksheet(Of String) = _ workbook。GetSheet(Of String)(WorksheetIdentifiers。Configuration) The cast has not disappeared pletely。 It is done for us in the implementation of the GetSheet(Of BaseType)() method; as demonstrated by the following code。 Friend Class Workbook Implements IWorkbook; IDebug Private _worksheets As IDictionary(Of String; IWorksheetBase) = _ New Dictionary(Of String; IWorksheetBase)() 。 。 。 Public Function GetSheet(Of BaseType)(ByVal identifier As String) _ As IWorksheet(Of BaseType) Implements IWorkbook。GetSheet SyncLock _worksheets Dim retval As IWorksheet(Of BaseType) = Nothing If _worksheets。ContainsKey(identifier) Then retval = TryCast(_worksheets。Item(identifier); _ IWorksheet(Of BaseType)) Else retval = New Worksheet(Of BaseType)(identifier) _worksheets。Add(identifier; retval) End If …………………………………………………………Page 324…………………………………………………………… 302 CH AP T E R 1 1 ■ L E A R N I N G A B OU T 。 N E T G E N E R I CS Return retval End SyncLock End FunctionEnd Class The bolded code shows that there is still a cast; but the type cast is in the method; and it uses the generics parameter declared at the method level。 ■Note The SyncLock keyword ensures that the code in this method is not executed by more than one thread at a time。 That way; the calling code always gets back the correct sheet。 Implementing the Server Spreadsheet Now let’s look at how the workbook and worksheet are implemented。 I will explain only the important pieces; but all of the code is available for download from the Source/Downloads area of the Apress web site (http://apress。)。 The class Worksheet(Of BaseType) imple ments the interface IWorksheet; but does not specify on which type the worksheet should be based。 In this application; we also have a class called TraderBaseClass。 In most applications; there is some functionality that most classes will need。 That mon functionality is what I call a domain…specific base class。 In the case of TraderBaseClass; that is the implementation of the IDebug interface。 Using Lambda Expressions in the Spreadsheet The data members of Worksheet(Of BaseType) are very similar to the previously defined spread sheet class; except the declarations are lambda…ready。 Lambda…ready means that you use the Func(Of ) type whenever you want to declare a variable that references a lambda expression。 The following three data members are used to store the state of the cell; cell calculations that will calculate the state of a cell; and cell calculations that calculate the state of cells for an entire column。 Private Cells As Func(Of IWorksheet(Of BaseType); Integer; Integer; BaseType)(;) Private CellState As BaseType(;) Private ColCells As