VBA composition for java-like inheritance - vba

Expanding a bit on this question VBA inheritance pattern
I'm reproducing a basic inheritance pattern in VBA,
but I would like to understand If there is a more efficient and terse way to accomplish that.
Consider this little test-case.
IAnimal.cls
'declaration
Public Sub eat()
End Sub
Public Sub breathe()
End Sub
Animal.cls : the superclass
Implements IAnimal
' method implementation
Private Sub IAnimal_eat()
Debug.Print "I'm eating something..."
End Sub
Private Sub IAnimal_breathe()
Debug.Print "I'm brething..."
End Sub
Cat.cls : a subclass of Animal
Private super As IAnimal
Implements IAnimal
Private Sub Class_Initialize()
Set super = New Animal
End Sub
'#------------------------------------------------
' methods called when object is accessed as an IAnimal implementor.
' I HAVE TO re-implement all of them also here in the subclass (in java I don't need to. It's sufficient to implement them in the superclass)
Private Sub IAnimal_eat()
Me.eat
End Sub
Private Sub IAnimal_breathe()
Me.breathe
End Sub
'#--------------------------------------------------
' subclass-only methods
' To access those methods I MUST DECLARE the object as Cat (Dim tom as Cat)
Public Sub meow()
Debug.Print "meow..."
End Sub
'#------------------------------------------------
' superclass methods
' Since I need to declare the cat object as a Cat (see above)
' I'm FORCED TO explicitly re-implement all of the superclass methods,
' even those that I don't need to override
' otherwise I can't access them
'#Override
Public Sub eat()
Debug.print "I'm eating a fish!"
End Sub
'I'm forced to re-implement also this method, in order to use it directly on a *Cat* object
'#Unnecessary-Override
Public Sub breathe()
super.breathe
End Sub
Test.bas
Sub Main()
Dim snowball As IAnimal
Set snowball = New Cat
Dim tom As Cat
Set tom = New Cat
snowball.meow 'ERROR Method or data member not found <---- cannot access the Cat-only method "meow"
tom.meow '<--- prints "meow"
' creates a Dog, another subclass of Animal
Dim pluto As Dog
Set pluto = New Dog
'create a collection for all my animals
Dim myPets As Collection
Set myPets = New Collection
myPets.Add tom
myPets.Add pluto
Call feed(myPets) '<---- prints
'I 'm eating a fish
'meow...
'I 'm eating a bone
'woof...
End Sub
' a routine for showing how to manage an heterogeneous collection of animals
Sub feed(animals As Collection)
For Each a In animals
a.eat
If TypeOf a Is Cat Then
a.meow
End If
If TypeOf a Is Dog Then
a.woof
End If
Next
End Sub
By doing this, I can:
call the eat() method on the Cat and Dog objects and have the specific Overridden behavior
call the subclass-only methods (like meow())
pass an heterogeneous collection of Animals to a feed subroutine, that can "safely" call the Animal superclass methods and also trigger specific code based on the Animal subclass
This seems to work but it's cumbersome: imagine you need to implement many Animal subclasses (Dog, Bird, Armadillo, Platypus, Demogorgon,...).
The pattern above FORCES YOU TO:
re-implement all the methods of the IAnimal interface on ALL THE SUBCLASSES
re-implement (again) all the methods, to expose them from the subclass, even when override is not necessary. This is required expecially if you want to access ALSO the subclass-only methods.
So the question is: is there a more efficient/concise way to implement this example (and limit code rewriting for every subclass) ?

tom shouldn't be declared As Cat in the first place; the feed procedure is superfluous:
Sub Main()
Dim tom As IAnimal
Set tom = New Cat
tom.eat
End Sub
Now, in the Cat class, these members shouldn't need to exist:
'Superclass methods --- I have to explicitly override all methods :(
Public Sub eat()
super.eat
Debug.print "...a fish!"
End Sub
In SOLID/OOP, you code against the interface, not the concrete type - that's why tom is an IAnimal and not a Cat. Being accessed through its IAnimal interface, Cat.eat is entirely redundant, and suggests that a Cat does something an IAnimal doesn't do, which violates SOLID principles: suddenly it becomes relevant that an IAnimal is a Cat, and it shouldn't be, because polymorphism allows IAnimal to be anything, and the Liskov Substitution Principle (LSP - the "L" of "SOLID") says any code that works with an IAnimal should be able to work identically regardless of what implementation of that interface it's given.
Abiding by these principles means that no IAnimal implementation should have a copy of IAnimal members on its default interface (e.g. Cat.eat, vs IAnimal.eat), and that entirely removes your point #2:
re-implements (again) all the methods, to expose them from the subclass, even when override is not necessary.
As for point 1...
re-implement all the methods of the IAnimal interface
That's a compiler requirement, and isn't a VBA quirk: be it in Java, C#, or VBA, you can't say "I'm implementing an interface" ...without implementing its members. Of course Java & C# allow for class inheritance, so your base class could say "I'm implementing an interface", implement all the members, and the derived classes would happily inherit them - but then, that's inheritance, not composition anymore.

Related

VBA Factory with VB_PredeclaredId = True vs "anonymous" instance pros and cons

Curios what are advantages/disadvantages between Factory with VB_PredeclaredId = True and "anonymous" instance in VBA. Each for its own scenarios or better stick to one of them and why? Any feedback or links where I can read more about it would be appreciated! Thank you!
Worksheet data staring A1
Name Manager Target
Person1 Manager1 97
Person2 Manager2 92
Person3 Manager3 95
Person4 Manager4 105
Person5 Manager5 108
Person6 Manager6 88
Factory - Class
'#Folder("VBAProject")
Option Explicit
'#PredeclaredId
Public Function getPerson(ByVal name As String, ByVal manager As String, ByVal target As Double) As Person
With New Person
.name = name
.manager = manager
.Targer = target
Set getPerson = .self
End With
End Function
Person - Class
Private pName As String
Private pManager As String
Private pTarger As Double
Public Property Get Targer() As Double
Targer = pTarger
End Property
Public Property Let Targer(ByVal value As Double)
pTarger = value
End Property
Public Property Get manager() As String
manager = pManager
End Property
Public Property Let manager(ByVal value As String)
pManager = value
End Property
Public Property Get name() As String
name = pName
End Property
Public Property Let name(ByVal value As String)
pName = value
End Property
Public Function toString() As String
toString = "Name: " & Me.name & ", Manager: " & Me.manager & ", Targer: " & Me.Targer
End Function
Public Function self() As Person
Set self = Me
End Function
Test - Module
Sub test()
Dim i As Long
For i = 2 To 6
With New Person
.name = Range("A" & i)
.manager = Range("b" & i)
.Targer = Range("c" & i)
Debug.Print .toString
End With
Debug.Print Factory.getPerson(name:=Range("A" & i), _
manager:=Range("B" & i), target:=Range("C" & i)).toString
'or shorter whithout feild names
Debug.Print Factory.getPerson(Range("A" & i), Range("B" & i), Range("C" & i)).toString
Next i
End Sub
TL;DR: It's apples and bananas, really. Anonymous objects are a language constructs; factories aren't defined in the language specs, they're more of a design pattern, and yes, there are different reasons to use each different approach - although IMO a "factory bag module" is a bad idea.
Anonymous Objects
Anonymous objects are awesome - you get to invoke members on an object that's held by a With block, without needing to add a local variable. That's With New, but it's also particularly useful with With CreateObject:
With CreateObject("Scripting.FileSystemObject")
'...
End With
They create objects, but such objects are (normally - a .Self getter can thwart that) confined to the containing With block; anonymous objects are useful for objects you need right here & now, and no longer need beyond that point. They are a language feature, basically.
Factory Class (or module)
We're leaving the realm of language features, and entering that of design patterns. Now we're talking about encapsulating the relatively complex creation of a given object, inside a class dedicated to that purpose.
The VB_PredeclaredId = True attribute (whether set by a #PredeclaredId Rubberduck annotation or not) makes that class work like any other standard module.
The problem with a Factory class that exposes a getPerson (or CreatePerson) method, is that you now have an excuse to extend this with some getAnotherThing (or CreateAnotherThing), and before you know it you're looking at a class/module that's able to create just about anything, whether these things are even remotely related or not.
Surely there's a better way.
Factory Method
What if a Person object knew how to create a new instance of the Person class?
Set p = Person.Create(1188513, "Mathieu")
This requires having the VB_PredeclaredId = True attribute on the Person class, and Person's default interface to expose a Create method to be invoked from that default instance.
The problem is that now anything that consumes a Person object is now seeing a confusing API that exposes Property Let members for ID and Name properties, and a Create function that's only meant to be used from the default instance.
This problem has a solution though: the only code that's "allowed" to work with Person, should be code that's responsible for invoking the Create factory method on that type. Everything else only ever sees an IPerson - an explicit interface that Person implements to define how the rest of the code shall interact with an object of that type... and IPerson does not need to expose any Property Let members, or Create method.
There's another problem that the pattern doesn't solve though - imagine IPerson is implemented by two or more classes (say, Employee and Manager): even if the entire project only ever sees IPerson, there's still some code in there that's calling Employee.Create or Manager.Create, and that code is inherently coupled with these specific implementations of the IPerson interface. It's a problem, because such coupling essentially negates the benefits of coding against interfaces in the first place.
Abstract Factory
In order to decouple the code that's creating IPerson objects from Employee and Manager classes, we could create an IPersonFactory interface that abstracts the factory method itself: now the code consuming the factory and create IPerson objects doesn't even know what concrete type of objects it's creating, and the decoupling is complete.
You probably don't need that level of decoupling unless you have everything covered with a thorough suite of unit tests, but it's useful to know it exists regardless.
So you would have EmployeeFactory and ManagerFactory classes, both implementing IPersonFactory and supplying an implementation for some Create function that returns an IPerson object.
...to be honest this is where the person/employee example kind of falls apart, because such a class is more of a simple DTO (Data Transfer Object - i.e. a bunch of public fields, or read/write properties) than anything that actually has responsibilities - so let's drop it.
We'll have a SqlConnectionFactory, an OracleConnectionFactory, and a MySqlConnectionFactory, all implementing some IDbConnectionFactory to yield some IDbConnection object that encapsulates, you'll have guessed, a database connection.
So we could have code that looks like this:
Public Sub DoSomething(ByVal factory As IDbConnectionFactory)
Dim pwd As String
pwd = InputBox("SA Password?") 'bad example: now we're coupled with the InputBox API!
Dim conn As IDbConnection
Set conn = factory.Create("server name", "sa", pwd)
'...
End Sub
That DoSomething method would be able to do its thing against an SQL Server, Oracle, or MySQL database server, and never needs to care which one it's actually working with.
Abstract Factory is useful when you're injecting dependencies (c.f. "dependency injection") that themselves have dependencies that you cannot resolve until last-minute, for example because you need some parameter that the user needs to provide through some UI (which is itself ideally also abstracted behind an interface, so that you can unit-test the method without popping that UI - although, Rubberduck's Fakes API does allow you to hijack an InputBox... imagine we're popping a UserForm instead).

VBA inheritance

I heard a lot of that VBA is lacking inheritance. I did a little workaround and now it seems to me that it is exactly what inheritance does. I'm far from pro =) and may be missing something. So I would really appreciate your thoughts about possible downsides.
I was very surprised when found that you still can make a full realization of a function in an interface class (not only a signature), which led me to the following below. I saw that some people did similar with help of composition, but they used only a signature in the interface.
IBird - class
Public Sub SayFromInterface()
Debug.Print "Tweet from IBird"
End Sub
Public Sub SayFromInstance()
End Sub
Crow - class
Implements IBird
Private pBird As IBird
Private Sub Class_Initialize()
Set pBird = New IBird
End Sub
'let you use already implemented code from "abstract class", to avoid
'duplicating your code, which is the main point of inheritance
'in my opinion
Public Sub IBird_SayFromInterface()
pBird.SayFromInterface
End Sub
'you can override the IBird function (common use)
Public Sub IBird_SayFromInstance()
Debug.Print "Tweet from Crow"
End Sub
Test - module
Sub testIBird()
Dim Bird As IBird
Set Bird = New Crow
Bird.SayFromInterface
Bird.SayFromInstance
Debug.Print TypeName(Bird)
Debug.Print TypeOf Bird Is IBird
End Sub
Output:
Tweet from IBird
Tweet from Crow
Crow
True
That's composition, not inheritance - and yes, with composition you can essentially emulate inheritance. And if the class implements the interface of the encapsulated object, then things start looking like some kind of a decorator pattern.
Except you wouldn't be having any implementation code in IBird. An interface should be purely abstract. Making a New instance of what's supposed to be an interface, makes the class no longer be an interface: now it's just another class exposing a default interface that any other class can implement, and the I prefix becomes rather confusing:
Set pBird = New IBird
It's rather weird that the client code now needs to wonder whether they want that bird to chirp FromInstance or FromInterface - these are very "meta" identifiers that make things not work like inheritance.
If we had a Crow : Bird where Bird had this implementation for IBird.Chirp:
public virtual string Chirp() => "Chirp!";
...and then Crow had this:
public override string Chirp() => "Craaaw!";
Then which method is invoked depends on what the runtime type is - this should feel pretty obvious:
IBird bird1 = new Bird();
bird1.Chirp(); // "Chirp!"
IBird bird2 = new Crow();
bird2.Chirp(); // "Craaaw!"
However picture a method that receives an IBird parameter:
public void DoSomething(IBird bird)
{
Debug.Print(bird.Chirp());
}
If bird is a Bird, it prints "Chirp!"; if bird is a Crow, it prints "Craaaw!": the method that gets to run, is the most derived override, which isn't necessarily defined on the most derived type.
Inheritance would allow a GiantCrow : Crow to inherit the Chirp method from Crow and not necessarily override it. And that is what you can't simulate with VBA classes: you are forced to write the equivalent of...
public override string Chirp() => base.Chirp();
...which is technically redundant, and gets very repetitive if you have to do it every time just to get the "base" members visible on your default interface.
Instead of inheriting base members, we actually wrap calls to the encapsulated object. The decorator pattern does exactly that, and gives you a non-intrusive way of extending a VBA class or interface.
A decorator implements the interface it's extending, and encapsulates a private instance of that type. So basically with a decorator the "crow inheritance hierarchy" setup looks like this:
Dim bird As IBird
Set bird = Crow.Create(New BaseBird)
A perhaps more suitable decorator pattern example might be:
Dim repository As IRepository
Set repository = LoggingRepository.Create(BirdRepository.Create(connectionString), New DebugLogger)
Where a BirdRepository is responsible for abstracting the database operations relating to some Birds table (BirdRepository implements IRepository), and where LoggingRepository is a decorator that also implements IRepository, but also wraps an IRepository instance (in this case a BirdRepository) to add its own functionality - something that might look like this:
'#PredeclaredId
Implements IRepository
Private loggerInternal As ILogger
Private wrappedInternal As IRepository
Public Function Create(ByVal internal As IRepository, ByVal logger As ILogger) As IRepository
Dim result As LoggingRepository
Set result.Wrapped = internal
Set result.Log = logger
Set Create = result
End Function
Public Property Get Wrapped() As IRepository
Set Wrapped = wrappedInternal
End Property
Public Property Set Wrapped(ByVal value As IRepository)
If Not wrappedInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
Set wrappedInternal = value
End Property
Public Property Get Log() As ILogger
Set Log = loggerInternal
End Property
Public Property Set Log(ByVal value As ILogger)
If Not loggerInternal Is Nothing Then Err.Raise 5, TypeName(Me), "Instance is already initialized."
Set loggerInternal = value
End Property
Private Function IRepository_SelectAll() As Object
Log.Info "Starting IRepository.SelectAll"
Dim t As Double
t = Timer
Set IRepository_SelectAll = wrappedInternal.SelectAll
Log.Info "IRepository.SelectAll completed in " & Timer - t & " seconds."
End Function
Private Sub IRepository_Delete(ByVal id As Long)
Log.Info "Starting IRepository.Delete(" & id & ")"
Dim t As Double
t = Timer
wrappedInternal.Delete id
Log.Info "IRepository.Delete completed in " & Timer - t & " seconds."
End Sub
Private Sub IRepository_Save(ByVal entity As Object)
'...
wrappedInternal.Save entity
'...
End Sub
'...
A method that is given a IRepository object cannot (and absolutely should not) know whether it's given a plain BirdRepository, a LoggingRepository wrapping a BirdRepository, or a FakeRepository that encapsulates a Collection instead of access to a database table - and this polymorphism is the entire point.
It's one way to extend a type without using inheritance, that VBA can absolutely leverage without bastardizing the pattern too much. But it's not inheritance.

How do I avoid down-casting to the interface class?

I'm using Excel VBA (Excel 2010) and I've run into a problem when attempting to use inheritance. Basically, I have an interface MyInterface and an implementing class MyImplementation. In the VBA code, when I refer to a Dim of type MyInterface I can only access members defined on that interface - this is expected. When I refer to a Dim of type MyImplementation I cannot access members defined on the interface it implements - not expected.
Why can I not call the interface property directly on the implementation class?
MyInterface
Option Explicit
Public Property Get Text() As String
End Property
MyImplementation
Option Explicit
Implements MyInterface
'The implementation of the interface method'
Private Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
Public Property Get MoreText() As String
MoreText = "Yes, some more text!"
End Property
MainModule - usage examples
Function Stuff()
Dim impl As New MyImplementation
Dim myInt As MyInterface: Set myInt = impl
'The following line is fine - displays "Yes, some more text!"
MsgBox impl.MoreText
'This is also fine - displays "Some text"
MsgBox DownCast(impl).Text
'This is also fine - displays "Some text"
MsgBox myInt.Text
'This is *not* fine - why??
MsgBox impl.Text
End Function
Function DownCast(ByRef interface As MyInterface) As MyInterface
Set DownCast = interface
End Function
The main question is how can I avoid down-casting?
Note - the example above is intentionally contrived. I realize it's generally bad practice to refer directly to implementation classes.
When I refer to a Dim of type MyImplementation, I cannot access members defined on the interface it implements - not expected.
The solution is to change your expectation. That's the way things work in VBA: VBA classes implement COM interfaces (such as IUnknown) without exposing them publicly.
If you want to expose your interface's members from the class, you have to do so explicitly:
Option Explicit
Implements MyInterface
'The implementation of the interface method'
Private Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
Public Property Get MoreText() As String
MoreText = "Yes, some more text!"
End Property
Public Property Get Text() As String
Text = MyInterface_Text
End Property
Simply declare the implementing methods as Public instead of Private will do:
Option Explicit
' Class MyImpl
Implements MyInterface
'The implementation of the interface method'
'Notice the Public here instead of private'
Public Property Get MyInterface_Text() As String
MyInterface_Text = "Some Text"
End Property
The only thing to keep in mind is that to invoke the methods on the implementation you'll need to use a longer name:
Dim instance as MyImpl
' initialize your instance
instance.MyInterface_Text
' instead of instance.Text
That's all.

VBA inheritance, analog of super

For example I have class A which implements class B
---class A----
implements B
public sub B_do()
end sub
--class B----
public sub do()
end sub
How can I call do() from A? (super.do()) So, how I can define some common variable for both classes? Now I can inherit only functions, sub and properties...
added: same question http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/5a83d794-3da1-466a-83d3-5d2eb0a054b2
added: It is not possible to share variable across hierarhy of classes. You should implements property (same way as functions).
The usual way to do this in VBA is to have A contain an instance of B as well as having A implement B's interface, and then delegate calls to the B interface of A to the internal B.
This is old stuff, but see the Visual Studio 6.0 Programmer's Guide:
http://msdn.microsoft.com/en-us/library/aa716285(VS.60).aspx
There is a chapter on "The Many (Inter)Faces of Code Reuse" that describes this convention:
http://msdn.microsoft.com/en-us/library/aa240846(v=VS.60).aspx
The way MS describes it is:
In addition to implementing abstract
interfaces, you can reuse your code by
implementing the interface of an
ordinary class, and then selectively
delegating to a hidden instance of the
class.
This means that implementation inheritance requires lots of explicit delegation methods. There's even a chapter subheading: "Doesn't This Get Tedious?". Yet another reason why OOP in VBA is a PITA (TM)...
EDIT THAT WON'T FIT IN A COMMENT:
To answer the question you posed in your comment, well, an A is a B. When you make A implement B's interface, you are essentially saying that you can treat an instance of A as if it is actually of type B. In VBA, the way you do that is by declaring a variable of type B, and then setting it to an instance of A. VBA will know what to do when you call it like a B:
Dim usedAsB as B
Dim anA as A
Set anA = New A
Set usedAsB = anA 'fine since A implements B
usedAsB.something() 'will call B_something() defined in class A
As far as what you see in the debug window, I don't why it appears that way. And as far as forced delegation, I'm not sure what you mean. VBA automatically dispatches calls to the B interface to the right methods in the A class. If you mean automatically generating the code to inherit B's implementation in the manner described above, there's nothing like that I know of for VBA. I think the various "professional" versions of VB6 could do that, but I've never used VB6 so I don't know.
This is the way I have used it a long time to simulate an abstract class through the interface.
'class module: IBase
'We define a base interface
'
Sub go(): End Sub
Sub gogo(): End Sub
Now let's define the other classes, beginning with the abstract class 'B'.
'
'class module: B
'
Implements IBase
'Daughter classes should implement 'go'
'Note that the method is 'Public'
Public Sub go(): End Sub
'Let's agree that every daughter class implements the method
'abstract 'super' that returns the IBase interface
Public Property Get super() As IBase: End Property
'
'The signature of other abstract methods can be stated here
'
'Public Sub goGoChild(): End Sub
'Public Function goGoGoChild2(): End Function
'
'
'Note that the methods are accessible through the IBase interface
'
Private Sub IBase_go()
Debug.Print "B: super.go()"
End Sub
Private Sub IBase_gogo()
Debug.Print "B: super.gogo()"
End Sub
Let's create class 'A' which implements the abstract class 'B'
'
'class module: 'A'
'
'We started implementing the abstract class B
Implements B
'we define a private type 'myType'
Private Type myType
'variable that references an instance of 'B'
objB As B
'variable that references the interface of 'B'
objIB As IBase
End Type
'VBA version of 'this'
Private this As myType
'
'Every class that implements 'B' (abstract class)
'you must initialize in your constructor some variables
'of instance.
'
Private Sub Class_Initialize()
With this
'we create an instance of object B
Set .objB = New B
'the variable 'objIB' refers to the IBase interface, implemented by class B
Set .objIB = .objB
End With
End Sub
'Visible only for those who reference interface B
Private Property Get B_super() As IBase
'returns the methods implemented by 'B', through the interface IBase
Set B_super = this.objIB
End Property
Private Sub B_go()
Debug.Print "A: go()"
End Sub
'==================================================
'Class 'A' local method
Sub localMethod1()
Debug.Print "A: Local method 1"
End Sub
And finally, let's create the 'main' module.
Sub testA()
'reference to class 'A'
Dim objA As A
'reference to interface 'B'
Dim objIA As B
'we create an instance of 'A'
Set objA = New A
'we access the local methods of instance 'A'
objA.localMethod1
'we obtain the reference to interface B (abstract class) implemented by 'A'
Set objIA = objA
'we access the 'go' method, implemented by interface 'B'
objIA.go
'we go to the 'go' method of the super class
objIA.super.go
'we access the 'gogo' method of the super class
objIA.super.gogo
End Sub
And the output, in the verification window, will be:
A: Local method 1
A: go()
B: super.go()
B: super.gogo()
One can pull a trick to mimic inheritance. It works by using the default member property.
If you give your derived class a property called Super whose type is the superclass then make that the default member (by exporting and editing file to include Attribute Item.VB_UserMemId = 0, re-importing) then you can reach the superclass by just a pair of round brackets (which resolves to the default member).
This blog post gives the full details but author (disclosure, me) there uses 'Base' instead of 'Super'
Hopefully that syntax is tight enough for you.
I also point out that this does not expose all the base class's internal guts like C# does. This means my method does not suffer from the fragile base class problem. I.e. my method retains encapsulation making it better IMHO.

Polymorphic Behavior in VB6

I recently noticed the CallByName keyword in VB6.
Since this takes a object, procedure name, "call type" and arguments array, can this be used to "fake" some types of polymorphic behavior?
I can make 2 classes, class A and B, each with the same method Foo, and do:
Dim list As New Collection
Dim instanceA As New ClassA
Dim instanceB As New ClassB
Dim current As Object
Call list.Add(instanceA)
Call list.Add(instanceB)
For Each current in list
Call CallByName(current, "methodName", vbMethod)
Next
Anyone done this before? Problems? Horrible idea or genius idea? Implications? Unintended consequences?
Why fake polymorphism? VB6 has real polymorphism in the form of interfaces:
' Interface1.cls '
Sub Foo()
End Sub
' --------------------------------------------- '
' Class1.cls '
Implements Interface1
Private Sub Interface1_Foo()
? "Hello from class 1"
End Sub
' --------------------------------------------- '
' Class2.cls '
Implements Interface1
Private Sub Interface1_Foo()
? "Hello from class 2"
End Sub
' --------------------------------------------- '
' Module1.mod '
Dim x As Interface1
Set x = New Class1
Call x.Foo()
Set x = New Class2
Call x.Foo()
Although I agree with Mr. unicorn, I can't help but point out that CallByName is also unnecessary (in this case) because you can call the method using the standard method syntax and it will result in a late-bound (i.e. not resolved at compile-time) call:
...
For Each current In list
Call current.methodName
Next
The real use of CallByName is to reference method names/properties where the name comes from a (possibly calculated) string value...an absolute abomination, in my opinion.
If you are in a situation where you inherited a huge project with not a single interface in it (it sounds like you did), then CallByName is an awesome tool to fake polymorphism. I use it all the time - never had any issues whatsoever.