Private Cells As Func(Of IWorksheet(Of BaseType); Integer; Integer; BaseType)(;) Private CellState As BaseType(;) Private ColCells As Func(Of IWorksheet(Of BaseType); Integer; Integer; BaseType)() The data member CellState contains the state of the worksheet cell; and its type is BaseType; meaning that the type of the worksheet cell is whatever BaseType is declared as。 The data members Cells and ColCells are declared as lambda expression references; where there are three param eters and a return value。 Before I continue with the lambda expression explanation; I want to shift focus to illustrate a problem。 We are going to play a game of what animal am I; where we’ll use lambda expressions in conjunction with a closure。 The idea is to store the identifier of the animal in a closure and then return it when it’s requested。 …………………………………………………………Page 325…………………………………………………………… 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 303 Dim animals(1) As Func(Of String) Dim animal As String animal = 〃cow〃 animals(0) = Function() animal animal = 〃horse〃 animals(1) = Function() animal For Each callAnimal In animals Console。Write (〃(〃 & callAnimal() & 〃)〃) Next The example creates an array of lambda expressions (animals) where space for two lambda expressions is allocated。 The individual lambda expressions will reference a variable animal; which contains the animal of the lambda expression。 In the code; the lambda expressions are assigned when the state of animal changes from cow to horse。 Let’s say you run the code; and Console。WriteLine() generates its output。 What do you think the animals will be? Do you expect cow and horse; respectively? Here’s the output: (horse) (horse) The generated output is not what you would expect。 This demonstrates that lambda expres sions are stateless。 Yet in earlier explanations of lambda expressions and closures; I said that variables can be stored in lambda expressions。 Since animal is a value type; you would expect two copies of animal; right? Wrong。 Closures do work; and variable state is kept across lambda expressions。 The mistake in this code is that the same variable is referenced by two lambda expressions。 Thus; when the two lambda expressions execute; they will reference the same animal; which was last assigned to be a horse。 The code that solves the problem uses two unique variables; as follows: Dim animals(1) As Func(Of String) Dim animal1 = 〃cow〃 animals(0) = Function() animal1 Dim animal2 = 〃horse〃 animals(1) = Function() animal2 For Each callAnimal In animals Console。WriteLine(callAnimal()) Next …………………………………………………………Page 326…………………………………………………………… 304 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 This time when you run the code; you get the following output。 cow horse Returning to the spreadsheet example; the problem is that the behavior just described means this code won’t work: Dim myWorksheet As IWorksheet(Of Double) For row As Integer = 0 To 10 myWorksheet。AssignCellCalculation(row; 1; _ Function(worksheet; cellRow; cellCol) _ myWorksheet。GetCellState(row; 0) _ myWorksheet。Calculate(10; 0)) Next whereas this code would work: Dim myWorksheet As IWorksheet(Of Double) For row As Integer = 0 To 10 Dim temp = row myWorksheet。AssignCellCalculation(row; 1; _ Function(worksheet; cellRow; cellCol) _ myWorksheet。GetCellState(temp; 0) _ myWorksheet。Calculate(10; 0)) Next The difference between the two pieces of code is that the second one has the lambda expression that uses the variable declared temp in the context of a loop。 For each iteration of the loop; a new instance of temp is allocated; and hence each lambda expression has its own instance of the row。 Assigning State Without Knowing the Type When using generics types; one of the most mon problems occurs when you need to work with proper types。 In the implementation of the IWorksheet interface; it is necessary to implement the AssignCellState() method defined in the interface IWorksheetSerialize。 The explanation of IWorksheetSerialize is slightly plicated and relates to the problem of loading an IWorksheet without knowing the type。 Say that you are saving an IWorkbook with multiple IWorksheet instances。 Each IWorksheet instance is a specific type。 When you want to load an IWorkbook; how does the loader know which types there are? The answer is that the loader does not; and thus must first load a general type; and then make a specific cast。 Take a look at the serialization source code in the project Devspace。Trader。mon and the namespace Devspace。Trader。mon。ServerSpreadsheet。SerializerImpls; available as part of the down loadable code。 …………………………………………………………Page 327……………………………………………………………