tags:

views:

48

answers:

1

Hi,

I am trying to design a table type structure that contains rows and cells. I intend the cell class to have a value property. I'm trying to work out how I can create a value that can return some defined different types e.g integer, single, date, string. I'd like the cell value to be strongly typed, but I am unsure how best to get this working.

My thinking in code so far:

Public Class Cell
  Private _value as object
  Public Property Value as Object    // How can I define this so that it return a Type 
    Get                              // e.g. integer, string, etc
      Return _value 
    End Get
    Set(ByVal value as object)
      _value = value
    End Set
End Class

Public Class Row
  Dim _Cells as New List(Of Cell)
  Public Function AddCell(ByVal c as Cell) as Cell
     _Cells.Add(c)
     Return _Cells(_Cells.count - 1)
  End Function
+1  A: 

Here a simple solution using basic inheritance and even without generics. The idea is that you have a single parent class for all cells. And a different cell class per column type. This make possible to declare list of cells in a Row object. Because of there may be different cell types within a single row, you can't declare it (list of cells) as a list of some specific cell type. But you can use cell's parent class in this case. In short here is example:

Public Class Cell
End Class

Public Class Cell_Id
    Inherits Cell
    Public Value As Integer
End Class

Public Class Cell_Name
    Inherits Cell
    Public Value As String
End Class

Public Class Cell_MonthlyTax
    Inherits Cell
    Public Value As Double
End Class


Public Class Row
    Public Cells As New List(Of Cell)

    Public Sub AddCell(ByVal cell As Cell)
        Cells.Add(cell)
    End Sub
End Class


Module Module1

    Sub Main()
        Dim row1 As New Row()
        row1.AddCell(New Cell_Id() With {.Value = 1})
        row1.AddCell(New Cell_Name() With {.Value = "Name1"})
        row1.AddCell(New Cell_MonthlyTax() With {.Value = 12.2})
        Dim row2 As New Row()
        row2.AddCell(New Cell_Id() With {.Value = 2})
        row2.AddCell(New Cell_Name() With {.Value = "Name2"})
        row2.AddCell(New Cell_MonthlyTax() With {.Value = 47.9})
        Dim row3 As New Row()
        row3.AddCell(New Cell_Id() With {.Value = 3})
        row3.AddCell(New Cell_Name() With {.Value = "Name3"})
        row3.AddCell(New Cell_MonthlyTax() With {.Value = 73.3})

        Dim rows As New List(Of Row)(New Row() {row1, row2, row3})

        ' here you loop through rows, in each row select Cell at index 2, cast it down to a specific
        ' type (yes, you should tell to a compiler that you sure that Cell at index 2 is of Cell_MonthlyTax type)
        ' After casting you will get intellisence and see that Value is of Double type.
        ' In cellsOfColumn2 you will get an array of Double (this is not exactly array, but it doesn't matter in our case)
        Dim cellsOfColumn2 = From row In rows Select DirectCast(row.Cells(2), Cell_MonthlyTax).Value

        ' here you may work with array of values as you want, say calculate avarange value
        Dim result = cellsOfColumn2.Average()
    End Sub

End Module
Kamarey
Thanks for the fast reply. I though of going with generics, but then you end having to have each cell in the row of one type. I haven't coded up the full model but the type would actually relate to a column. The offset of the cell in the _cells collection would determine the column and thus the cell type so the generic approach would not work.
Andrew
Can you provide an example of how you want to use Row and Cell classes to get Cell's values? I think missing something in your scenario. In any case (using generics or the Object) you will require to cast the Value to a specific type, it will not be automatically casted down
Kamarey
Imagine a reportview class that contains a columns collection and a rows collection. You would add columns to the reportview with an AddColumn method (columns would hold meta data - e.g. columnWidth and headerText). To add a new row you would call reportview.newrow and inside this method you would create a row with a new cell for each column in the report view.
Andrew
AddColumn would throw an error if you try to add a column after one or more rows has been added.
Andrew
One more question: why do you want to replace Object in your question with something else? What problems it does in your case?
Kamarey
I want to make sure the value is typed as I want to be able to consolidate the values. My intention is to use linq something like this (don't take my linq as being accurate here - more pseudo code I am still learning linq)dim mylist as list(of integer) = (From r in rows select r.c(2).value)dim avg as integer = mylist.avg()I think I have a solution to my original problem. I'll use a generic class as you have described, but add AddCell(ByVal tct as TableCellType) and then based on the passed in instantiate the cell.
Andrew
I edited the answer
Kamarey
Thanks for the sample code. I am not sure that having an empty interface (base class) is the best coding practice in the world though. I tried out a solution with value as object last night and found that casting an array in linq is really easy myarry.cast(of integer).Average. I doubt this is the most effiecient way to do this as it would mean under the covers there would be 2 iterations through the array. It may be easier using an ArrayList for the cells collection as it will only be exposed to the outside world as a readonly collection anyway. Then the generic cell class should work.
Andrew