Inheritance for VBA code in MS Access - vba

I've started to learn VBA in Access. I have read that the language has no inheritance.
And then I read an example code which seems like it actually has inheritance:
Dim ctrl As Control
...
If TypeOf ctrl Is TextBox Then ...
If TypeOf ctrl Is ListBox Then ...
It seems to me as the TextBox, ListBox were inherited from the Control. Could somebody explain this?

No. They are not derived from the Control class. They implement Control's definition/ methods and properties signatures. The way TypeOf and Is operators work it's they check whether the instance of a class Implements one of 3 categories (listed below).
open a new workbook
Go to VBE and add
a class module and name it: MyClass
in the code view only add Implements MyInterface
a class module and name it: MyInterface
in the code view - do nothing/leave empty
a module and copy paste the below code and run it
Sub Main()
Dim cls As MyClass
Set cls = New MyClass
Debug.Print TypeOf cls Is MyClass
Debug.Print TypeOf cls Is MyInterface
End Sub
The result may be surprising
True
True
cls variable is of two types - MyClass and MyInterface
as you can see cls doesn't inherit nothing from MyInterface but definition. When using TypeOf and Is it actually shows true because MyClass implements MyInterface. Not because it's derived from the MyInterface class but because it implements it.
Now, suppose
result = TypeOf objectexpression Is typename
The TypeOf operator determines whether the run-time type of variable is compatible with typename. The compatibility depends on the type category of typename. There are three categories
Class objectexpression is of type typename or inherits from typename
Structure objectexpression is of type typename
Interface objectexpression implements typename or inherits from a class that implements typename
Specifically try to understand the 3rd category - Interface.
I think at this point you should really understand why TypeOf varName Is varType shows True for the TextBox and ListBox...
When you do VBA inheritance, you can only use Implements keyword to
implements a class definition. That is, the class to be implemented is
equivalent to C++/C#'s abstract class: only having property/method
definition.
Generally, your example isn't a form of a class polymorphism. Class polymorphism occurs when you are actually deriving an instance of one to class to another. This isn't the case. Even though TextBox and ListBox are both of a Control type they aren't actually derived from Control class. Note: they may as well be members of another collection - they would be TypeOf the higher in the object hierarchy type ( forms, also Component and IComponent becuase Forms implements that).

Related

VBA Static Class "WithEvents"?

Disclaimer - I am by no means a VBA expert; I am a hack.
However, I've got some class modules that are static (using Attribute VB_PredeclaredId = True
)
I'd like to define some custom events with them, too (Public Event Foo(ByVal Bar As Boolean); however, since I am not instantiating the class, I'm not finding any info on any "Attributes" that will include "WithEvents" when "PreDeclaredID" is true.
Yes, I can use the class without its being static; however, I'd prefer to find a way for it to be declared automatically WithEvents, if it's possible.
The class has static semantics, but it's not static in the static sense you're referring to.
The VB_PredeclaredId attribute set to True means the compiler generates a global (or project-scoped, if the class is private) instance that is named after the class module itself.
In other words, there is literally an object/instance named Class1 (assuming the class module is named Class1), exposing the default interface defined by the Class1 module (i.e. it's an object whose compile-time type is Class1 regardless of what other interfaces that class might be implementing).
So you are not instantiating it, but the VBA compiler does.
And that object behaves every single bit the same as any other object you might have - there is no reason a Public Event could not be declared in it, and you can Set a WithEvents object variable to that "free" global instance, which you can refer to by name from anywhere in the project:
Private WithEvents Thing As Class1 '<~ requires Public Event declaration in Class1.
Private Sub Class_Initialize()
Set Thing = Class1 '<~ will not compile unless Class1 has VB_PredeclaredId=True.
End Sub

Is it possible to determine the type of a class at instantiation or convert the type afterwards without casting?

For example: I have some classes that all inherit from the same class.
Public Class MasterClass
' content
End Class
Public Class ClassA
Inherits MasterClass
'content
End Class
Public Class ClassB
Inherits MasterClass
'content
End Class
Public Class ClassC
Inherits MasterClass
'content
End Class
And I want decide on runtime which one I need. Then I can do something like this:
Private myInstance As MasterClass
If conditionA Then
myInstance = New ClassA
ElseIf conditionB Then
myInstance = New ClassB
Else
myInstance = New ClassC
End
But it can get quite long, and I still have to cast it evertime I use it.
I can assing a type to a variable, but I don't know how to use that type to create a new instance of that type..
Dim storedType As Type = GetType(ClassA)
Dim someInstance = New storedType 'Does not work
Is there a better way? Can you change the type of a variable at runtime?
I still have to cast it evertime I use it.
The idea around polymorphism and inheritance is that you don't have to cast them to use them. You can write things in such a way that the master class has all the functions etc that you need (whether or not they do anything) and then you call things as if you were just dealing with the master class but, because each child implements a different behavior, the end result is different - your program might not even know (if the child implementation came from a third party dll) what is going on but it doesn't matter
Can you change the type of a variable at runtime?
Sure, but you have to use it how it appears. Long chains of "if my object is an instance of x then cast my object as x and use method X1, else if my object is a y then cast as y and call y1" are not polymorphic/not leveraging inheritance principles properly - you're supposed to call myobject.whatever, and if my object is an x, then x1 happens and if it's a y then y1 happens
I want decide on runtime which one I need
But you don't have to do that in the class that knows about class a/b/c - each of class a/b/c can do that and hence become self contained. You can have all your instances in an array of the parent type, and visit each one asking them if they handle the condition and use the one that says it can
Consider a slightly better real world example than this artificial class a/b/c trope:
You are tasked with writing an app that can download an image (png, jpg or gif) from somewhere (http or ftp or disk location), rotate it and upload it to somewhere else
You decide to have an ImageRotator parent that specifies a CanHandle function and a Rotate function. It has 3 subclasses, one that handles jpg, one that rotates gif and one that does png. When presented with a PNG filename the JpgRotator says No when asked if it can handle it etc.
Separately you have a FileMover parent that CanHandle and has Download/Upload functions. Again, the parent doesn't implement these at all. The three subclasses implement the ability up/down an http, an ftp and a disk location
You create an instance of each rotator and put it into an array of ImageRotator type. You also create an instance of each mover onto an array that is a FileMover parent type.
Your user specifies a jpg in a http url, and to store it in a disk location at the end. You loop your FileMovers and ask each if they support the location the user provided. The http mover says yes, you invoke its download to a temp path. Then you pass he path to each rotator, the jpg rotator says yes, you call rotate. Finally, you look for another mover that can handle an output path of local disk...
Someone decides to extend your program with a plug-in dll that adds he ability to put files in and out of a db, and support tiff images.. ignoring the magic of how instances of their classes come to be in your arrays, you can see that your program can now move these new locations and types because the logic for whether they handle db/tiff is not a part of your code.. your code just treats everything consistently
In this case Interfaces are the best choice
Interface ABC
Property Text As String
Property Value As Integer
End Interface
Public Class MasterClass
End Class
Public Class ClassA
Inherits MasterClass : Implements ABC
Property Mystr As String Implements ABC.Text
Property Sum As Integer Implements ABC.Value
End Class
Public Class ClassB
Inherits MasterClass : Implements ABC
Property MyText As String Implements ABC.Text
Property Value As Integer Implements ABC.Value
End Class
Public Class ClassC
Inherits MasterClass : Implements ABC
Property str As String Implements ABC.Text
Property Value As Integer Implements ABC.Value
End Class
usage
Dim myABC As ABC
If conditionA Then
myABC = New ClassA
ElseIf conditionB Then
myABC = New ClassB
Else
myABC = New ClassC
End If
myABC.Text = "Interface"

Passing type argument to a generic custom class

I've seen a lot of chatter on this topic. Though the examples and desired outcomes are always very specific and specialized. Any direction on this is appreciated.
Custom Class:
Public Class customClass(Of T, S)
[include properties]
[include methods]
End Class
Implementation of Class:
dim [string] as string = string.empty
dim [type] as type = [string].gettype
dim instance as customClass(of [type], integer)
Also note, I've read that vb.net does not allow you to pass parameters to its constructor. I refuse to accept that you can't pass a type to a class and generate objects dependent on the type of that argument. Is the only answer to this a function in the class which returns a list of objects cast to the desired type? Your time is appreciated.
This question is motivated by academic research. The above is "what I am trying to do" thanks.
Kind of hard to see what you're trying to do, but if I'm reading this right, you're trying to take a variable and use that as the generic argument. This is not possible in .NET - when you declare a variable of a generic class, you need a compile-time type as the generic argument, so it cannot be the a variable of type Type.
This is important for a couple of reasons, one of which is to ensure that type constraints are met.
So:
Class Foo(Of T)
End Class
Dim x as Type = GetType(String)
Dim y as Foo(Of x)
does not work - you have to do:
Dim y as Foo(Of String)
There's always reflection and expression trees, but that's more of a hack than a solution.
You cannot use dynamic types to call this kind of typed declarations (Of t,s) but you can 'group' or delimit several types using interfaces or inheritance, which could also be very useful.
Class customClass(Of T As iMyInterface, s As iMyInterface)
End Class
Interface iMyInterface
End Interface
Class MyClass1
Implements iMyInterface
End Class
Class MyClass2
Implements iMyInterface
End Class
Dim y As New customClass(Of MyClass1, MyClass2)

Run-time error 459 when using WithEvents with a class that implements another

I am developing a VBA project in Word and have encountered a problem with handling events when using a class that implements another.
I define an empty class, IMyInterface:
Public Sub Xyz()
End Sub
Public Event SomeEvent()
And a class, MyClass that implements the above:
Implements IMyInterface
Public Event SomeEvent()
Public Sub Xyz()
' ... code ...
RaiseEvent SomeEvent
End Sub
Private Sub IMyInterface_Xyz()
Xyz
End Sub
If I create a third class, OtherClass, that declares a member variable with the type of the interface class:
Private WithEvents mMy As IMyInterface
and try to initialize this variable with an instance of the implementing class:
Set mMy = New MyClass
I get a run-time error '459': This component doesn't support this set of events.
The MSDN page for this error message states:
"You tried to use a WithEvents
variable with a component that can't
work as an event source for the
specified set of events. For example,
you may be sinking events of an
object, then create another object
that Implements the first object.
Although you might think you could
sink the events from the implemented
object, that isn't automatically the
case. Implements only implements an
interface for methods and properties."
The above pretty much sums up what I'm trying to do. The wording, "that isn't automatically the case", rather than "this is flat-out impossible", seems to suggest that there is some bit of manual work I need to do to get it to work, but it doesn't tell me what! Does anybody know if this is possible in VBA?
Apparently Events are not allowed to be passed through an interface class into the concrete class like you want to using "Implements". In this article it states: "Event declarations of the abstract interface are not included in the interface that is inherited by concrete classes. I haven't found anywhere that this has been acknowledged as a bug; however, it does seem to be one."
Here is the link to the source: http://www.devx.com/getHelpOn/10MinuteSolution/20416
:-(

Type parameters cannot be used as qualifiers

VB.Net2005
Simplified Code:
MustInherit Class InnerBase(Of Inheritor)
End Class
MustInherit Class OuterBase(Of Inheritor)
Class Inner
Inherits InnerBase(Of Inner)
End Class
End Class
Class ChildClass
Inherits OuterBase(Of ChildClass)
End Class
Class ChildClassTwo
Inherits OuterBase(Of ChildClassTwo)
End Class
MustInherit Class CollectionClass(Of _
Inheritor As CollectionClass(Of Inheritor, Member), _
Member As OuterBase(Of Member))
Dim fails As Member.Inner ' Type parameter cannot be used as qualifier
Dim works As New ChildClass.Inner
Dim failsAsExpected As ChildClassTwo.Inner = works ' type conversion failure
End Class
The error message on the "fails" line is in the subject, and "Member.Inner" is highlighted. Incidentally, the same error occurs with trying to call a shared method of OuterBase.
The "works" line works, but there are a dozen (and counting) ChildClass classes in real life.
The "failsAsExpected" line is there to show that, with generics, each ChildClass has its own distinct Inner class.
My question: is there a way to get a variable, in class CollectionClass, defined as type Member.Inner? what's the critical difference that the compiler can't follow?
(I was eventually able to generate an object by creating a dummy object of type param and calling a method defined in OuterBase. Not the cleanest approach.)
Edit 2008/12/2 altered code to make the two "base" classes generic.
Dim succeeds as OuterBase.Inner
.net does not have C++'s combination of template classes and typedefs, which means what you are trying to do is not possible, nor does it even make sense in .net.
ChildClass.Inner and SomeOtherChildClass.Inner are the same type. Here's a short but complete program to demonstrate:
Imports System
MustInherit Class InnerBase
End Class
MustInherit Class OuterBase
Class Inner
Inherits InnerBase
End Class
End Class
Class ChildClass
Inherits OuterBase
End Class
Class OtherChildClass
Inherits OuterBase
End Class
Class Test
Shared Sub Main()
Dim x as new ChildClass.Inner
Dim y as new OtherChildClass.Inner
Console.WriteLine(x.GetType())
Console.WriteLine(y.GetType())
End Sub
End Class
The output is:
OuterBase+Inner
OuterBase+Inner
What were you trying to achieve by using "parameterised" nested classes? I suspect that either it wouldn't work how you'd want it to, or you can achieve it just by using OuterBase.Inner to start with.
Now if each of your child classes were declaring their own nested class, that would be a different situation - and one which generics wouldn't help you with.