worksheet and define the actual type using generics。 The problem with this solution is that a workbook would define a collection of mixed types。 You might be tempted to believe that Worksheet(Of Double) and Worksheet(Of String) are of the type Worksheet(Of BaseType); and thus are all a single type。 This is not the case; because with generics; a type that hasn’t been concretized is not a type at all。 Think of it as being an almost type; and to make the program work; you need to concretize everything。 Figure 11…2 shows two concrete types: Worksheet(Of Double) and Worksheet(Of String)。 These are two different types。 The two different types make it plicated for the workbook; because the workbook wants to maintain a single collection of worksheets。 If we assume for the moment that the worksheet interface is defined as follows: Interface IWorksheet(Of BaseType) End Interface the workbook could reference the worksheet as this collection: Dim _worksheets As List(Of IWorksheet(Of BaseType)) However; that reference is inplete; and the piler would want to know what BaseType references。 To keep your options open; one solution is to not plete the BaseType; but let the user of workbook figure things out; thus defining workbook as follows: Class Workbook(Of BaseType) Dim _worksheets As List(Of IWorksheet(Of BaseType)) End Class …………………………………………………………Page 317…………………………………………………………… 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 295 This solution seems to be a good one; but; in fact; it’s passing the buck。 The solution does not address the problem of Figure 11…2 and forces the end user to solve it。 The core problem is that Figure 11…2 uses generics to define worksheets of specific types; which means mixed types that need to be addressed by the workbook。 In other words; a workbook can contain only spreadsheets of a certain type; as in this example: Workbook(Of String) workbook1 Workbook(Of Double) workbook2 It would seem that generics make everything more plicated。 However; there’s more to this than first appears。 generics have not made things more plicated; but rather have required us to be more explicit about what we actually want to do。 We want to be able to define specific work sheet types; which means we have a mixed list of types that the workbook must manage。 As explained in Chapter 9; non… generics list types cannot control whether or not a list contains mixed types。 To solve the worksheet problem; we need to put on our object…oriented thinking caps。 First; what is a worksheet? It’s a spreadsheet that fulfills the role of a two…dimensional thing; and it applies to all worksheets regardless of types。 Therefore; the first interface to define is a base worksheet; as follows (do this in the ServerSideSpreadsheet project; as well as adding a reference to the Devspace。Trader。mon project): Imports Devspace。Trader。mon Public Interface IWorksheetBase Inherits IDebug Sub Dimension(ByVal rows As Integer; ByVal cols As Integer) ReadOnly Property MaxCols() As Integer ReadOnly Property MaxRows() As Integer End Interface The interface definition of IWorksheetBase has one method and two properties。 The method Dimension() is used to assign the maximum rows and columns of the individual spreadsheet。 The properties MaxRows and MaxCols return the maximum rows and columns。 The properties and method have nothing to do with the specific type managed by the worksheet; but the inter face manages to uniquely identify the instance as being a type of spreadsheet。 In the workbook code; the list of worksheets would be defined as follows: Private _worksheets As IDictionary(Of String; IWorksheetBase) = _ New Dictionary(Of String; IWorksheetBase) Now the workbook knows it has a series of worksheets; but the workbook does not know or care about the types of the worksheets。 When users of the workbook want to manipulate an individual worksheet; they can retrieve the worksheet from the workbook; but the users need to know the worksheet’s type。 The spreadsheet is typically addressed using rows and columns; but to simplify declara tions; there is also the ability to define something called the SheetCoordinate; which is a type that has a row and column。 The SheetCoordinate is defined in ServerSideSpreadsheet as follows: …………………………………………………………Page 318…………………………………………………………… 296 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 Public Structure SheetCoordinate Public Row As Integer Public Column As Integer Public Sub New(ByVal row As Integer; ByVal column As Integer) If (row 《 0) Then Throw New ArgumentOutOfRangeException(〃Row is below zero〃) End If If (column 《 0) Then Throw New ArgumentOutOfRangeException(〃Column is below zero〃) End If Me。Row = row Me。Column = column End Sub Public ReadOnly Property OneUp() As SheetCoordinate Get Return New SheetCoordinate((Me。Row 1); Me。Column) End Get End Property Public ReadOnly Property OneDown() As SheetCoordinate Get Return New SheetCoordinate((Me。Row + 1); Me。Column) End Get End Property