Coding Best Practices Part 2

Friday, 14 January 2022 by Gavin Huet

This blog outlines coding practices that any professional developer using the finPOWER Connect API should adhere to.

finPOWER Connect is written entirely in VB.NET. All sample scripts are also written in VB.NET. Although C# is an option, Intersoft Systems does not provide support for this.

In this second blog we will look at performance.


Related Blogs


More information is available in the finPOWER Connect Programming Guide.

Top Best Practices

AndAlso and OrElse

Always use AndAlso and OrElse instead of And and Or. They shortcut expression evaluation and make for more optimised and robust code.

For example, the example below would cause an error since you are trying to divide 100 by zero. This is because using an And still evaluates the second part of the expression even if the first part fails.

  Dim i As Integer
  
  If i <> 0 And 100/i < 20 Then
    ' Do Something
  End If

Using AndAlso does not cause an error and is also more efficient.

  Dim i As Integer
  
  If i <> 0 AndAlso 100/i < 20 Then
    ' Do Something
  End If

Caching Values

Certain operations may be expensive i.e. slow, or use a lot of processing power, or database querying. In these circumstances caching values in variables is recommended instead of accessing the multiple times.

Consider the following code:

  Dim Account As finAccount
  Dim Message As String
  Dim Success As Boolean

  ' Assume Success
  Success = True
  
  ' Load Account
  Account = finBL.CreateAccount()
  Success = Account.Load("L10000")

  ' Create Message
  If Success Then
    If TotalByElementType(isefinElementType.InterestDefault) = 0 Then
      Message = "No Default Interest charged."
    Else
      Message = String.Format("Default Interest charged to date {0}.", finBL.FormatCurrency(TotalByElementType(isefinElementType.InterestDefault), True))
    End If
  End If

Getting the Total Payments may be slow. This code can be optimised by caching this in a local variable:

  Dim Account As finAccount
  Dim Message As String
  Dim TotalDefaultInterest As Decimal
  Dim Success As Boolean

  ' Assume Success
  Success = True
  
  ' Load Account
  Account = finBL.CreateAccount()
  Success = Account.Load("L10000")

  ' Create Message
  If Success Then
    TotalDefaultInterest = Account.Calculation.Schedule.TotalByElementType(isefinElementType.InterestDefault)

    If TotalDefaultInterest = 0 Then
      Message = "No Default Interest charged."
    Else
      Message = String.Format("Default Interest charged to date {0}.", finBL.FormatCurrency(TotalDefaultInterest, True))
    End If
  End If

Other places where you should cache values include:

  • Loops, e.g.
    • Don't recalculate values or concatenate Strings unnecessarily within loops. Calculate the value before entering the loop.
    • Don't call functions within loops unnecessarily. If the function's return values will not vary with each iteration of the loop, call the function before entering the loop and store the result in a variable. For example:
  Dim i As Integer
  Dim kvl as ISKeyValueList

  For i = 0 to 100
    kvl = finBL.CreateKeyValueList()

    ' Do Something
  Next

Reinitialising the ISKeyValueList object on each iteration of the loop is expensive. This can be optimised by inititialising the object once and then clearing it on every iteration of the loop:

  Dim i As Integer
  Dim kvl as ISKeyValueList

  ' Initialise
  kvl = finBL.CreateKeyValueList()

  For i = 0 to 100
    kvl.Clear()

    ' Do Something
  Next

Pass Objects instead of reloading them

Loading an object takes time, therefore objects should be passed between functions in preference to reloading them.

Consider the following code:

Private Sub LoadAccount(accountId As String)

  Dim Account As finAccount
  Dim ClientList As String
  Dim Success As Boolean
  
  ' Assume Success
  Success = True
  
  ' Load Account
  Account = finBL.CreateAccount()
  Success = Account.Load("L10000")
  
  ' Get Names of all Account Clients
  If Success Then
    ClientList = GetClientList(Account.AccountId)
  End If
  
End Sub

Private Function GetClientList(accountId As String) As String

  Dim Account As finAccount
  Dim AccountClient As finAccountClient
  Dim ClientList As String
  Dim Success As Boolean

  ' Assume Success
  Success = True

  ' Load Account
  Account = finBL.CreateAccount()
  Success = Account.Load(accountId)
  
  ' Get Names of all Account Clients
  If Success Then
    For Each AccountClient In Account.Clients
      If Len(ClientList) <> 0 Then ClientList &= vbNewLine
      ClientList &= AccountClient.ClientName
    Next
  End If
  
  Return ClientList
  
End Function

Exceptions to this rule may include:

  • Where a global or module variable already exists holding the object (e.g. a finPOWER Connect Summary Page Script). In these cases there is no need to pass the object around at all since it will be available to all functions.
  • Where a function must have the latest version of the object as stored on the database e.g. to ensure it has not been changed by the User or to ensure it contains changes made elsewhere.

WARNING: Using global and module variables for performance reasons can make for confusing code and should be used only when necessary.