How do I write a VB class which allows indexing into/calling an instance? - vb.net

I'm honestly not sure what this is called, and if I did I'm sure I could google it in about 5 seconds.
I want to be able to write a class that I can sort of "index into" the way you do with a collection, eg:
Public Class FooClass
Public Function magicKeyword(param as String) as String
If param = "foo" Then
Return "bar"
Else
Return "baz"
End If
End Function
...
End Class
And then use it like this:
Dim myObj as New FooClass
Dim output as String = myObj("foo") '<-- this is what I want to know how to do
' output = "bar"
What is this called, and what syntax would I use for the function?
Out of curiosity, can this also be done as a Shared function so the class itself can do it? e.g.:
Dim output as String = FooClass("foo")

What you are describing is implementing a custom collection, and Microsoft provides a pretty good guide on how to accomplish this.
It should be said, however, that the collection classes already provided in .NET should be able to handle most use cases. A truly custom collection is overkill in many instances.

Related

Sort a list by a function results

I want to sort a list by the results of functions that are contained in list items. How would go about doing that?
Here is an example. Let's say I have a following object:
Public Class MyListObject
Public MyText1 As String
Public MyText2 As String
Public Function AddSuffix(ByVal MySuffix As String) As String
Return Mytext1 & MySuffix
End Function
End Class
(massively oversimplified for the sake of example)
After that I have a list of these objects:
Dim ResultList As New List(Of MyListObject)
Now, for example, to sort the list by the field MyText1 value, I used this:
ResultList = ResultList.OrderBy(Function(x) GetType(MyListObject).GetField("MyText1").GetValue(x)).ToList
(used reflection, so I can pass the field name as a string)
How can I sort this list by the function AddSuffix result and simultaneously pass the parameter MySuffix?
I imagine it would look something like this, but obviously this doesn't work
ResultList = ResultList.OrderBy(Function(x) GetType(MyListObject).GetField("AddSuffix(""_myCustomSuffix"")").GetValue(x)).ToList
PS. I realize, that in this example the function is pretty much meaningless. The actual code is much more complex and that function does return different sortable data for each list item.
I am guessing you have a pretty good reason to use reflection, but I would recommend avoiding it even if you have too many fields and methods.
ResultList = ResultList.OrderBy(Function(o) o.AddSuffix("_myCustomSuffix")).ToList
The VB way of calling a method by name is something like this (also not tested)
ResultList = ResultList.OrderBy(Function(o) CallByName(o,
"AddSuffix", CallType.Method, "_myCustomSuffix")).ToList

VB.net anonymous class with inheritance

Imagine I have class like this :
Class A
Public Function Some(str As String) As String
Return "Some " + str
End Function
End Class
I have consuming code like this :
Public Sub Foo()
Dim thisWorks = New With {.prop = "thing"}
Dim thisDoesntWork = New inherits A with { .prop = Some("thing") }
End Sub
I'm trying to create an anonymous type with inheritance so that I can use the methods within. Is this possible ?
Use case : I'm trying to create a class that has methods like Select, From etc. that would help in cleaner query construction. In the consuming code, I would just create an anonymous type inheriting from the class and use the methods.
What you want is not possible (at least not the way you describe it).
From what I think I understood ; you should try to mimic what has been donne for Linq ; an interface (like IEnumerable) with all the method you want (or maybe an abstract class bu that prevent you to inherit from something else) + something else (probably a module if you want them as extension method) defining the Select etc. acting on the interface.
From that point you can create classes which implement the interface and use your custom Select etc. on them

vbscript static class variables/methods?

Is there a way to have one variable per class in vbscript?
If not what is the best way to emulate it? Prefixing a global variable declared next to the class?
Also is there a way to declare static/class methods(for a static constructor) or am I force to prefix a function?
In languages that support class-level/static data or methods you can
associate/bind data or methods explicitly to the set of objects defined by the class. So you can have Customer.Count and Product.Count and a plain Count (or ##Count) in Customer code will access the right number.
use such data or method without having an instance of the class (yet).
VBScript does not support static data or methods. You have to use global data or functions/subs and do the associating in your mind (perhaps with a little help from a naming convention). Accessing these 'static'=global elements without an object is trivial, but - obviously - should be done with care.
You can embed one or more singleton objects or code references (GetRef()) in your objects to bind them closer to the class, but that will increase the size of the instances.
You can do something like this to sort of emulate a static class:
Class Defines_
Public Sub DoSomethingUseful
End Sub
End Class
Dim Defines : Set Defines = New Defines_
...
Defines.DoSomethingUseful
This can be used to give you something analogous to constructors (really, factory methods):
Class Something
Private mValue
Public Property Get Value : Value = mValue : End Property
Public Property Let Value(x) : mValue = x : End Property
End Class
Class SomethingFactory_
Public Function Create(value)
Set Create = New Something
Create.Value = value
End Function
End Class
Dim SomethingFactory : Set SomethingFactory = New SomethingFactory_
...
Dim something : Set something = SomethingFactory.Create(5)

How to load a class into the current instance within Sub New

Long term lurker, first time poster here.
I have written a class to model an object in vb.net, using vs2008 and framework 2.0. I am serializing the class to an XML file for persistent storage. I do this with a method in the class like this:
Public Sub SaveAs(ByVal filename As String)
Dim writer As New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file As New System.IO.StreamWriter(filename)
writer.Serialize(file, Me)
file.Close()
End Sub
I now want to do a similar thing but reading the class from file to the current instance, like this:
Public Sub New(ByVal filename As String)
Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file = New System.IO.StreamReader(FullPath)
Me = CType(reader.Deserialize(file), MyNamespace.MyClass)
End Sub
However, I cannot assign anything to “Me”. I’ve tried creating a temporary object to hold the file contents then copying each property and field over to the current instance. I iterated over the properties (using Reflection), but this soon gets messy, dealing with ReadOnly collection properties, for example. If I just copy each property manually I will have to remember to modify the procedure whenever I add a property in the future, so that sounds like a recipe for disaster.
I know that I could just use a separate function outside the class but many built-in .NET classes can instantiate themselves from file e.g. Dim bmp As New Bitmap(filename As String) and this seems very intuitive to me.
So can anyone suggest how to load a class into the current instance in the Sub New procedure? Many thanks in advance for any advice.
I'd put a shared load function on the class, that returned the newly de-serialised object.
e.g.
Public Class MyClass
...
Public shared Function Load(ByVal filename As String) as MyClass
Dim reader = New Xml.Serialization.XmlSerializer(GetType(MyNamespace.MyClass))
Dim file = New System.IO.StreamReader(FullPath)
Return CType(reader.Deserialize(file), MyNamespace.MyClass)
End Sub
End Class
...
Dim mine as MyClass = MyClass.Load("MyObject.Xml");
Hope this helps
Alternatively,
Encapsulate the data of your class in an inner, private class.
The properties on your outer visible class delegate to the inner class.
Then Serialising and De-serialising happens on the inner class, you can then have a ctor that takes the file name, de-serialises the inner hidden object, and assigns it to the classes data store.
The "New" method in VB.Net is a constructor for the class. You can't call it for an existing instance, as the whole purpose of the method is to create new instances; it's just not how the language works. Try naming the method something like "ReadFrom" or "LoadFrom" instead.
Additionally, given those methods, I would try to implement them using a Factory Pattern. The ReadFrom method would be marked Shared and return the new instance. I would also make the method more generic. My main ReadFrom() method would accept an open textreader or xmlreader or even just a stream, rather than a file name. I would then have overloads that converts a file name into a stream for reading and calls the main method.
Of course, that assumes I use that pattern in the first place. .Net already has great support for xml serialization built into the platform. Look into the System.Xml.Serialization.XmlSerializer class and associated features.

vb.net - creating array/list from loaded interfaces

I'm having trouble creating a global array that i can use in other functions.
I have this code right under "Public Class myClass":
Dim LoadedPlugins As Array
Then in a function I have this:
Dim PluginList As String() = Directory.GetFiles(appDir, "*.dll")
For Each Plugin As String In PluginList
Dim Asm As Assembly
Dim SysTypes As System.Type
Asm = Assembly.LoadFrom(Plugin)
SysTypes = Asm.GetType(Asm.GetName.Name + ".frmMain")
Dim IsForm As Boolean = GetType(Form).IsAssignableFrom(SysTypes)
If IsForm Then
Dim tmpPlugin As PluginAPI = CType(Activator.CreateInstance(SysTypes), PluginAPI)
LoadedPlugins(count) = tmpPlugin
In the Interface file I have:
Public Interface PluginAPI
Function PluginTitle() As String
Function PluginVersion() As String
Function CustomFunction() As Boolean
End Interface
Now obviously that doesn't work, how can I add the tmpPlugin to an array or list so I can use it in other functions?
The main thing I need to be able to do is loop through all the loaded plugins and execute the CustomFunction in a separate function than the one that loads the plugins listed above.
Can anyone help me?
Use this for your dim:
Public Shared LoadedPlugins As Array
However I notice a few things
Some tips to make writing programs faster (in the end, but more thinking for design):
use Option Strict.
give LoadedPlugins a type (PluginAPI)
use a generic list instead of array
don't use globals. Figure out a way to give this info to the classes that need it
this is a bit of an art. But don't give up - 5 programs later you will have it down!