Extends Strings Class Functions without inheritance - string

I am thinking to extends the strings functions and add my own functions to the module. but I found that the strings class is non-inheritable.
is there any way to extends the class. I found something like :
Class MyStringType
Dim str As String
function toTitleCase()
'return titlecae
end function
End Class
Dim s As MyStringType
s.str = "mystringgoeshere"
but this does not extends in the proper way, it will results to :
S.Str.normalStringFunctions
S.MyOwnFunctionsHere
in result, the functions are not in the same level !!
any idea?

You can write Extension methods also in VB.NET
Module MyExtensions
<Extension()>
Public Sub toTitleCase(input As String)
....
End Sub
End Module
After this 'syntactic sugar' is in place you can write your code as
Dim newString = oldString.toTitleCase()
However, I would to remember you that ToTitleCase method is already available in the System.Globalization.TextInfo namespace

What you're after are extension methods.
Basically, you cannot extend System.String, but you can create subs or functions in modules and have the compiler "fake" they are string methods.

Related

Problem with defined type in Excel VBA Userform

I had found a lot similar topics but no solution only workarounds (many advices to use classes but I do not understand how to do it - I want it to be as simple and short as possible)
Private Type Bottom_Rit
Bot As Integer
Rit As Integer
End Type
Dim BxR_1 As Bottom_Rit
Sub Fpage()
BxR_1 = Lab(a, b)
End Sub
Private Sub Button1_Click()
Fpage
End Sub
Function Lab(a As Integer, b As Integer) As Bottom_Rit
Lab.Bot = a
Lab.Rit = b
End Function
Trying to repeat the code from this thread link to stackoverflow thread
I get an error message "Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions"
User Defined Type (UDT)
If you define a UDT within a class you have the following restrictions
You cannot use a public UDT in a class and a userform is a kind of class.
You cannot use a UDT as return type in a public function in a class.
You cannot use a UDT as a parameter in a public function in a class.
You can use a UDT in that class locally (i.e use the keyword Private to define it)
The code from the post is used in a userform therefore the OP has to define the UDT as Private and every function needs also to be private in case the UDT is used in the signature of the function.
That means the following code will work
Private Type Bottom_Rit
Bot As Integer
Rit As Integer
End Type
Private Function Lab(a As Integer, b As Integer) As Bottom_Rit
Lab.Bot = a
Lab.Rit = b
End Function
PS I'd also recommend to use Option Explict. You can read about it in this post although not excatly for VBA but it covers it as well.

Is there an implemented function similar to the toString() function in vba?

I want to get an Object within a Collection as a String without calling a function for it.
e.G.:
In java i can
System.out.print(objVarXY)
and the compiler will automatically call the objVarXY.toString() function (if implemented)
in VBA something like this
Debug.Print parameterListe.LList.Item(1)
will cause an error.
Debug.Print parameterListe.LList.Item(1).toString
will work, if i implemented a toString Subfunction.
But what if i dont know what kind of object will be inside my LList collection?
Debug.Print will implicitly attempt coerce the given value expression into a String for output.
When you Debug.Print an object, VBA attempts to Let-coerce the object into a value - if the object doesn't have a default member that ultimately yields a value that can be implicitly converted to a String, then you get run-time error 438 "object doesn't support this property or method" if the class doesn't have a default member.
If the object is user code (i.e. your own class module), and if it makes sense to do so, you could add a default member yourself, and have the class responsible for knowing how to represent itself as a String (note that the VB attribute is hidden in the VBE code panes, and must be edited outside the VBE - unless you're using Rubberduck, in which case you can simply add a #DefaultMember annotation and synchronize annotations/attributes):
'#DefaultMember
Public Function ToString() As String
Attribute ToString.VB_UserMemId = 0
'...
End Function
But all this does is give the class the ability to be essentially treated as a String, through implicit member calls. I'd call that something like an abuse a language feature (it's through this mechanism that Debug.Print Excel.Application outputs the application's Name, or Debug.Print adoConnection outputs the connection's ConnectionString property), since as you noted, you might as well just invoke that ToString method explicitly.
If the object doesn't know how to represent itself as a String, then something, somewhere will have to. In Java (IIRC) and .NET this would essentially be the default ToString implementation:
Debug.Print TypeName(objVarXY)
...which is rather useless, but that's essentially what ToString does by default.
Whether you're writing Java, C#, or VBA, there needs to be code responsible for knowing how to represent objVarXY as a String.
Sadly VBA doesn't do fancypants pattern matching, so we can't Select Case TypeOf obj like in C# (it only recently got that ability - don't know about Java), and since Select Case TypeName(obj) wouldn't be type-safe, I'd go with If...ElseIf:
Public Function Stringify(ByVal obj As Object) As String
If TypeOf obj Is Something Then
Dim objSomething As Something
Set objSomething = obj ' cast to Something interface
Stringify = objSomething.SomeProperty
ElseIf TypeOf obj Is SomethingElse Then
Dim objSomethingElse As SomethingElse
Set objSomethingElse = obj ' cast to SomethingElse interface
Stringify = objSomethingElse.AnotherProperty & "[" & objSomethingElse.Foo & "]"
'ElseIf TypeOf obj Is ... Then
' ...
Else
' we don't know what the type is; return the type name.
Stringify = TypeName(obj)
End If
End Function
Obviously if the collection always involves classes that you own, the better solution is to have each object know how to represent itself as a String value.
But, having user classes expose a ToString method on their default interface isn't ideal: since we don't know what type we're getting from the collection, all we have is Object and a late-bound call - and no compile-time guarantee that the class implements a ToString method, and no compiler warning if we try to invoke, say, ToStrnig.
The solution is to not put ToString on the classes' default interface, and formalize the behavior with some IString class module, which might look like this:
Option Explicit
Public Function ToString() As String
End Funtion
Yup, that's the whole class.
Now the user classes that need to be representable as strings, can do this:
Option Explicit
Implements IString
Private Function IString_ToString() As String
' todo: implement the method!
End Function
And now we can have early-bound assurance that the objects have a ToString method:
Dim o As Object
For Each o In MyCollection
If TypeOf o Is IString Then
Dim s As IString
Set s = o 'cast to IString interface
Debug.Print s.ToString
Else
Debug.Print TypeName(o)
End If
Next
At the end of the day there's no magic, regardless of what language you're using.
Something like that does not exist in VBA there are only the Type conversion functions like CStr() to convert eg. an Integer into a String.
If you eg need to convert a Collection into an Array you will need to use a function for that.
But what if I don't know what kind of object will be inside my LList collection
Then you will need to determine which object it is (you would probably expect eg 5 different possible objects) and do something like a Select Case for each different object type to convert this to a String.

Do I need the Me keyword in class modules?

These two subs do the same thing when inside a class.
Sub DemoMe( )
Me.AboutMe ' Calls AboutMe procedure.
End Sub
Sub DemoMe( )
AboutMe ' Does the same thing.
End Sub
What is the point? Does the Me keyword do anything? What is the preferred way of an object accessing its own members?
tldr; No, although there are situations where it can be useful.
From the VBA language specification (5.3.1.5):
Each procedure that is a method has an implicit ByVal parameter called
the current object that corresponds to the target object of an
invocation of the method. The current object acts as an anonymous
local variable with procedure extent and whose declared type is the
class name of the class module containing the method declaration. For
the duration of an activation of the method the data value of the
current object variable is target object of the procedure invocation
that created that activation. The current object is accessed using the
Me keyword within the <procedure-body> of the method but cannot be
assigned to or otherwise modified.
That's all it is, just a "free" local variable that refers to the specific instance that the method is being called on. This also happens to be the default context for the procedures during their invocation, so it can be omitted if the code is intended to operate on the current instance. Although as #HansPassant points out in the comment above, it also allows the editor to bind to the interface and provide IntelliSense.
That said, there are a couple instances where you would either want to or have to use it (this is by no means an exhaustive list):
Naming collisions:
If your class has a member that "hides" a built-in VBA function, it can be used to make the scope explicit:
Public Property Get Left() As Long
'...
End Property
Public Property Get Right() As Long
'...
End Property
Public Property Get Width() As Long
Width = Me.Right - Me.Left
End Property
Equity Checks:
Public Function Equals(other As Object) As Boolean
If other Is Me Then
Equals = True
Exit Function
End If
'...
End Function
Fluent Functions:
This can be a useful pattern for compositing objects - you perform an action, then return the instance of the class so they can be "chained". Excel's Range interface does this in a lot of cases:
Public Function Add(Value As Long) As Class1
'Do whatever.
Set Add = Me
End Function
Public Sub Foo()
Dim bar As New Class1
bar.Add(1).Add(1).Add 1
End Sub
Not any more than there are reasons to use this in Java, C#, or any other language: it's a reserved identifier that represents the current instance of the class - what you do with that is up to your imagination.
What is the preferred way of an object accessing its own members?
Indeed, an object doesn't need the Me keyword to access it own public interface. Same as this in other languages, I'd even call it redundant. However it can sometimes be a good idea to explicitly qualify member calls with Me, especially when the class has a VB_PredeclaredId attribute (e.g. any UserForm): referring to UserForm1 in the code-behind of UserForm1 yields a reference to the default instance of the class, whereas qualifying member calls with Me yields a reference to the current instance of that class.
Accessing Inherited Members
VBA user code can't do class inheritance, but a lot of VBA classes do have a base class. The members of UserForm when you're in the code-behind of UserForm1, and those of Worksheet when you're in the code-behind of Sheet1, aren't necessarily easy to find. But since the inherited members show up in IntelliSense/auto-complete, you can type Me. and browse a list of members inherited from the base class, members that you would otherwise need to know about in order to invoke.
A class creating an instance of itself inside itself? That I've never seen.
You're missing out! I do this all the time, to enable referring to the object instance held by a With block, inside a Factory Method - like this GridCoord class.
Public Function Create(ByVal xPosition As Long, ByVal yPosition As Long) As IGridCoord
With New GridCoord
.X = xPosition
.Y = yPosition
Set Create = .Self
End With
End Function
Public Property Get Self() As IGridCoord
Set Self = Me
End Property
Note that while the GridCoord class exposes a getter and a setter for both X and Y properties, the IGridCoord interface only exposes the getters. As a result, code written against the IGridCoord interface is effectively working with read-only properties.
Another use is to get the name of the class module, without needing to hard-code it. This is particularly useful when raising custom errors: just use TypeName(Me) for the Source of the error.
The Builder Pattern notoriously returns Me, which enables a "fluent API" design that makes it possible to write code that incrementally builds complex objects through chained member calls, where each member returns Me (except the final Build call, which returns the type of the class being built):
Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
.WithFoo(42) _
.WithBar("test") _
.WithSomething _
.WithSomethingElse
.Build
#PBeezy : In addition to my comment :
Me, refers to the object it's coming from so AboutMe resides in the class. If you had another instance, say this is Class1, you'd have dim c as Class1, as soon as you create an instance of Class1 in Class1, you need to tell the compiler which class you are using, the holding class or the instance created in, where, me.class1.aboutme would be logically valid. You can also create, a class for each cell in a workbook, then you could refer to A1's class from B1's class. Also, if there is a public function/sub called AboutMe, this also helps.
Class (clsPerson)
Public c1 As clsPerson
Public strPersonName As String
Public Function NAME_THIS_PERSON(strName As String)
strPersonName = strName
End Function
Public Function ADD_NEW_CHILD(strChildName As String)
Set c1 = New clsPerson
c1.strPersonName = strChildName
End Function
Normal module
Sub test()
Dim c As New clsPerson
c.NAME_THIS_PERSON "Mother"
c.ADD_NEW_CHILD "Nathan"
Debug.Print c.strPersonName
Debug.Print c.c1.strPersonName
End Sub
Gives these results
Mother
Nathan

Type Mismatch Error when Passing Child Type to Subroutine accepting Parent Type VBA

Context
I have a parent interface, IParent,
Option Explicit
Public Sub DoParentStuff()
End Sub
a child interface implementing IParent, IChild,
Option Explicit
Implements IParent
Private Sub IParent_DoParentStuff()
End Sub
Public Sub DoParentStuff()
End Sub
and a concrete implementation of IChild, CStandardChild.
Option Explicit
Implements IChild
Private Sub IChild_DoParentStuff()
End Sub
Public Sub DoParentStuff()
IChild_DoParentStuff
End Sub
I then created a module that passes a variable of type IChild to a subroutine with one parameter of type IParent.
Option Explicit
Private Sub Test(ByRef parent As IParent)
parent.DoParentStuff
End Sub
Public Sub Main()
Dim child As IChild
Set child = New CStandardChild
Test child
End Sub
I can compile the VBA project without error. But, when I run Main, I get a run-time error
Run-time error '13':
Type mismatch
The debugger points to the code Test child.
Question
Why am I getting a run-time, type-mismatch error? How can I pass child to Test() without getting this error?
What I've Tried
I've looked into casting IChild to IParent. However, I'm not using VB.NET, so, I don't have access to DirectCast and CType. In saying this, if I've implemented IParent and IChild appropriately, I didn't think a cast is necessary.
If I understand correctly what you're trying to do, it looks like you're trying to extend IParent with IChild members. You can't do that in VBA - it would be awesome, but it's part of what makes .NET a more flexible framework to work with.
To take a C# analogy - this is what I think you're trying to do (and is illegal in VBA):
interface IFoo { void DoSomething() }
interface IBar : IFoo { void DoStuff() } // inherits members of IFoo
If you need CStandardChild to be accessible through both IParent and IChild interfaces, you need both Implements statements in that class:
Option Explicit
Implements IChild
Implements IParent
'implement members of both interfaces...
Then you can pass an instance of that class and "cast" it to either interface.

Worksheets vs. Worksheets(1), can't I do this from .net interop?

Our object model contains a class called Unit and a collection of these called Units (which is stored in a Dictionary). These objects have unique Names and Keys (they originally came from a SQL db that enforced this) so I have added:
Public Units(N as String) As Unit ...
Public Units(K as Integer) As Unit...
which return a Unit object from the Units collection.
In Excel VBA, one can refer to most objects using similar methods; Worksheets(1) returns the first sheet, while Worksheets("Bob") returns the named sheet. But they have one additional method, Worksheets, which returns the entire collection. It's as if they have this method...
Public Worksheets() As List(Of Worksheet)
But you can't use List in interop (right?) so it's more like...
Public Worksheets() As ArrayList
So how would I do the same basic API in .net with interop? That is, have three methods...
Public Units(N as String) As Unit ...
Public Units(K as Integer) As Unit...
Public Units() As ArrayList...
As I understand it only the first method of a given name is exported (is this correct?). So how does Excel do it, and can I fake that in .net?
VBA's Worksheets is not a method. It is a class, Worksheets, that has a default property Item that accepts a parameter of type Variant. There is no overloading (COM does not support it), it's just that Variant can hold both a number or a string.
If you want a similar structure in VB.NET, you can have a collection class that implements a default property as VB.NET understands it, and this time you can overload it.
Public Class UnitsCollection
Default Public ReadOnly Property Item(ByVal i As Integer) As Unit
Get
Return ...
End Get
End Property
Default Public ReadOnly Property Item(ByVal i As String) As Unit
Get
Return ...
End Get
End Property
End Class

Resources