vb.net

Dynamically adding to a VB.net Object


I'm working with MiniExcel, trying to save values into a template--not really relevant except to explain why I need to use these specific types (Dictionary/Object). This is my VB.net conversion of some of the MiniExcel sample C# code, which works great:

Dim value As New Dictionary(Of String, Object)()
value("rows") = New Object() {
    New With {.name = "Jack", .department = "HR"},
    New With {.name = "Lisa", .department = "HR"}
}
MiniExcel.SaveAsByTemplate(path, templatepath, value)

What I need to do is loop through some data and add to value("rows") dynamically instead of typing out static values like this, but I don't know the right words to search to figure out how to add to an Object this way.


Solution

  • In this case, the () at the end of the Object() expression is part of the type name rather than a constructor method call and indicates you have an array.

    In .NET, we have real arrays rather than the pseudo-array collections you get on many other platforms (.NET has those, too, but calls them what they are). This means .NET arrays have a fixed size. Therefore, to add to the array you have to allocate a whole new array and copy the data (this is what ReDim Preserve does behind the scenes).

    That's crazy-inefficient. Instead, I recommend converting this to a List (which also tends to allocate new buffers and copy behind the scenes as you add items, but does it less often and gives you a better API for the process):

    value("rows") = New List(Of Object) From {
        New With {.name = "Jack", .department = "HR"},
        New With {.name = "Lisa", .department = "HR"}
    }
    

    Now you can append with code like this:

    Dim rows As List(Of Object) = DirectCast(value("rows"), List(Of Object) )
    rows.Add(New With {.name = "New Name", .department = "New Dept" } )
    

    But it's very strange in .NET to rely on the Object type like this in the first place. You should mentally wince in discomfort every time you need to type Object into your code and should really have Option Strict or Option Infer turned on (leaving it off is really only for compatibility when porting forward legacy vb6-era code) and define actual types for this data... especially the contents of the rows collection. This helps avoid things like the ugly DirectCast() call in the code above. If every entry will have a name and department property, you can make things waaaaaay easier on yourself by defining a short class for this:

    Public Class Employee
        Public Property Name As String = ""
        Public Property Department As String = ""
    
        Public Sub New(Name As String, Department As String)
            Me.Name = Name
            Me.Department = Department
        End Sub
    End Class
    

    And then use it like this:

    Dim value As New Dictionary(Of String, Object)()
    value("rows") = New List(Of Employee) From {
        New Employee("Jack", "HR"),
        New Employee("Lisa", "HR")
    }
    

    The constructor is optional; you could still use an object initializer here if you wanted instead, but I'm old-school that way.


    Even better if you can use a real type instead of the Dictionary, but I understand you may be at the mercy of the MiniExcel library.