17

In VBA, I can create objects one of two ways:

'First way
Dim myCol1 As New Collection

'Second way
Dim myCol2 As Collection
Set myCol2 = New Collection

myCol1.Add "AAA"    'Works
myCol2.Add "BBB"    'Works as well

Is the second way just a more verbose version of the first way, or is there actually a difference between the myCol1 and myCol2 objects?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Combinatix
  • 1,186
  • 3
  • 12
  • 24
  • The second version really just splits the operations into two lines. It wouldn't surprise me if deep enough in the compiled operations there's no difference at all in the sequence of events taking place. – David Mar 07 '17 at 19:17
  • I think so. I prefer the first way (shorter), but what confuses me is so many examples on the internet using the second way ?! – Combinatix Mar 07 '17 at 19:21
  • 4
    There is a subtle but important difference in that `as new` defers creation until the first (and all subsequent) calls across the reference variable, see [What is the reason for not instantiating an object at the time of declaration?](http://stackoverflow.com/questions/8114684/what-is-the-reason-for-not-instantiating-an-object-at-the-time-of-declaration) – Alex K. Mar 07 '17 at 19:21
  • 3
    @David Unfortunately, there's a huge difference in how it is implemented. There are actually 2 different op-codes, and 2 different functions inside MSVBVM. – ThunderFrame Mar 07 '17 at 19:43
  • 1
    @ThunderFrame: I stand corrected, learn something new every day I guess. (Though, being VB, I sincerely hope I *never* need to apply this knowledge. Just saying.) – David Mar 07 '17 at 19:44

2 Answers2

30

There are several key differences. You should definitely prefer the second Dim/Set approach.

Reason 1 - With As New, the object doesn't get created until a property or method of that object is called, but look at this example where setting the object to Nothing, and then calling a property/method causes the object re-instantiate itself:

Sub ShortcutInstantiation()

  Dim x As New Collection

  x.Add "FOO", "BAR"
  Set x = Nothing

  'This line implicitly recreates a new Collection
  Debug.Print x.Count

  Debug.Print x Is Nothing 'Prints False

End Sub

Sub SafeInstantiation()

  Dim x As Collection
  Set x = New Collection

  x.Add "FOO", "BAR"
  Set x = Nothing

  'Throws error because x is nothing
  Debug.Print x.Count

End Sub

Reason 2 The As New approach is slower, because VBA needs to check if it has instantiated the object before every single property or method call.

Look at this pseudo code to see what VBA is doing behind the scenes:

Sub NotSoShortcutInstantiation()

  Dim x As New Collection

  If x Is Nothing Then Set x = New Collection
  x.Add "FOO", "BAR"

  If x Is Nothing Then Set x = New Collection
  x.Add "FIZZ", "BUZZ"

  If x Is Nothing Then Set x = New Collection
  x.Add "CAR", "DOOR"

  If x Is Nothing Then Set x = New Collection
  Debug.Print x.Count

End Sub

Reason 3 There can be critical timing differences if you object constructor does something after you expect it to, rather than when you explicitly instantiate it:

Compare the results of this code:

Sub InstantiationTiming()

  Dim foo As String

  Dim x As New Class1
  Debug.Print Format(Now(), "hh:mm:ss") & " x should be ready"
  foo = x.foo

  Dim y As Class1
  Set y = New Class1
  Debug.Print Format(Now(), "hh:mm:ss") & " y should be ready"
  foo = y.foo

End Sub

The As New approach prints:

06:36:57 x should be ready
06:36:57 Class Initialized

The Set y = New approach prints:

06:36:57 Class Initialized
06:36:57 y should be ready
ThunderFrame
  • 9,352
  • 2
  • 29
  • 60
  • 2
    Also often see confusion with `as new` in a loop, which makes people think they are getting a new object when infact they are using exactly the same reference (and its state) each time round. – Alex K. Mar 07 '17 at 19:36
  • What is the purpose of the Dim As New then? It seems like it has only disadvantages? – Combinatix Mar 07 '17 at 19:54
  • It's less characters to type! i.e. it's never, ever worth the trade-off. – ThunderFrame Mar 07 '17 at 19:55
  • 2
    The answer says *"object doesn't get created until a property or method of that object is called"*, but it is any reference to the object in the code, not just calling its members. For instance, even the line `Debug.Print x is Nothing` will trigger creation of the object so that it always prints `False`. – C Perkins Apr 07 '19 at 18:51
7

The As New construct has legitimate uses. In a class, with a module level variable when you do not know which method will be called first then it saves some lines of code. So some code snippet I have lying around in a class is given here

Option Explicit

Private mdicQueryStringParams As New Scripting.Dictionary

Function SafeItem(ByVal sKey As String, ByRef pvItem As Variant) As Boolean

    If mdicQueryStringParams.Exists(sKey) Then
        pvItem = mdicQueryStringParams.Item(sKey)
        SafeItem = True
    End If

End Function

Imagine very many more methods that rely upon an initialised mdicQueryStringParams. You'd have to write guard code to ensure that it was created in all of these methods.

Now at this point you're saying but you can use Sub Class_Initialize to New up upon class creation. Like this

Private Sub Class_Initialize()
    Set mdicQueryStringParams = New Scripting.Dictionary
End Sub

But suppose I want to recycle/reset part of the class's state then I could write a Clear method which sets mdicQueryStringParams to Nothing . In this case Sub Class_Initialise won't run again. Here on SO Mat's Mug has taught me that Static classes are possible in VBA (thanks!) so sometimes Sub Class_Initialise will only run once.

(^ Admittedly I could set it to a New instance in the Clear method, yes, yes, I know, I know)

The point is with the As New syntax you get a resurrecting variable as well as auto-initialisation. Surely as developers this is another technique/pattern in our toolbox that we should exploit and not ban.

In truth, I use it infrequently but I just don't like banning stuff.

Community
  • 1
  • 1
S Meaden
  • 8,050
  • 3
  • 34
  • 65
  • Thanks. I agree it is potential trap. – S Meaden Mar 07 '17 at 20:38
  • 1
    Why would you have a static class that stores mutable state that needs to be "reset"? That strikes me as an oxymoron. – Comintern Mar 07 '17 at 20:46
  • This is interesting. I can see the reasons why I should use the Dim/Set approach (thank you for an excellent answers). But in everyday programming, when you make a function, you normally create the object, then you use it, and then you destroy it. What I'm saying is that the Reasons 1 in ThunderFrame's answer will hardly occur and the Reason 3 can occur only in a very specific code. And, when you make a Excel function with few calls, the performance difference is insignificant. I, however, see the point of what should I be aware and that's the reason why I have asked this question. – Combinatix Mar 07 '17 at 20:54
  • In the case of `Dictionary`, seems like your `Clear` method should be calling `Dictionary.RemoveAll()`. In the more general sense, if you're Newing up a field/private member, I'd still prefer that the class author manage the creation/destruction. Have `Class_Initilalize` call a `SetupNewDictionary` helper, and then if you really must destroy the original dictionary and create another, have the *destroying* method *also* call `SetupNewDictionary`. – ThunderFrame Mar 07 '17 at 20:57
  • @Comintern: On static state ... in C#, Java, C++ classes can have static members that is mutable state. The static state would pertain to the class and not the instance. So you could have a Frog class from which Frog instances are created and do Frog like things but say you want to keep note of how many Frog instances created then you'd use a static member for that. VBA does cannot mix static and non static in same class. Finding a more precise example maybe difficult. If it helps, I'm using "static" not as a synonym for immutable. I'm not talking about the Static keyword for a Local. – S Meaden Mar 07 '17 at 21:00
  • @Combinatix 1 and 3 may be edge cases (but they're easy traps to fall into, and debugging them can be really hard if you don't understand the implications of `As New`), but 2 will *always* be a performance hit. Accessing object properties in a loop will exacerbate that performance hit. – ThunderFrame Mar 07 '17 at 21:00
  • @Thunderframe: under the hood VBA is checking for a Null pointer, at the CPU level comparing a register for zero is an optimised CPU operation. Hardly matters. Admittedly in a loop of thousands, it matters then. – S Meaden Mar 07 '17 at 21:09