Posts filed under 'VB.NET Tutorials'
Enums, or enumerations, as you probably already know, allows you to create a set of logically related constant values. Using them means you can avoid hard coding numbers and you make your code more readable and maintainable because you’re using memorable and logical names for the values.
One problem that VB6 programmers often ran into was that there was no easy way to extract the matching string for a value. This typically led to clumsy conversion code and/or database tables that had to be maintained whenever the Enum changed. However, in .NET, we now have the System.Enum object to take care of this situation and more. In this article, we will take a look at some of the useful functions in this object.
GetName
The first function we’ll look at is GetName. As you might suspect, this returns the string associated with an Enum value. Here’s what the code looks like, first our example Enum declaration, followed by the GetName call.
Public Enum EmailTypes
Introduction
Sales_Flyer
Thank_You
Important_Notice
Holiday_Greetings
Other
End Enum
..............
EnumValue = System.Enum.GetName(GetType(EmailTypes), EmailTypes.ThankYou).Replace("_", " ")
In this case our return value would be “Thank_You”. I used the underscore in the enum name to make it easy to replace it with a space for display purposes. You can’t have a variable name, or an Enum member name, with a space.
GetNames
OK, that’s one value. What if you wanted them all so that you could add them to a combo or list box control? That’s what GetNames is for. Here’s the code, using our same example Enum above.
lstEmailType.Items.AddRange([Enum].GetNames(GetType(EmailTypes)))
.
.
.
Of course, this doesn’t remove the underscore from the strings but this would be easy enough to add if you needed it with a simple loop. Also note that we used “[Enum]” in the code above rather than System.Enum. Coding it in the square brackets shows that we’re using the object rather than the statement of the same name.
GetValues
Sometimes you might want to return all of the values in an Enum at once. For this, you would use the GetValues function as seen here:
Dim EmailValues As Array
EmailValues = [Enum].GetValues(GetType(EmailTypes))
For Each EmailValue As Integer In EmailValues
'more code goes here
Next
This function makes it easier for us to iterate through the values in an enum.
IsDefined
This function is used to determine if a string name or a numeric value is defined in the Enum. For example…
If [Enum].IsDefined(GetType(EmailTypes), "Thank_You") Then
............
If [Enum].IsDefined(GetType(EmailTypes), 9) Then
…the first If would evaluate to True since we do have an Enum named “Thank_You” in our example. The second would return False because we don’t have that value in our example Enum.
That’s all for this tutorial. There are a few more methods in the Enum object that you might want to explore on your own that I didn’t cover here. Let me know if you have any questions or observations about this article by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 17th, 2007
Most VB programmers, even those who’ve haven’t used VB.NET much, know that to create an object they invoke it by calling the class’s constructor using the New keyword. Those who’ve worked with VB.NET may know that there can be multiple constructors for a class and that the .NET Framework automatically builds a no-argument constructor behind the scenes for you at compile time if you don’t supply one. All of these constructors have a Public access modifier so that they can be created from outside the class. Sometimes, there will be cases where you only want one instance of a class in your application. This is where the Singleton Pattern comes into play.
Coding the Singleton Class
There are three steps to creating a simple Singleton class.
First, we will also want to have a module level Private Shared variable that holds the single instance we’re creating:
Public Class MySingleton
Private Shared _thisInstance As MySingleton
End Class
Next, we want to create a single constructor method with a Protected access modifier, like so:
Public Class MySingleton
Private Shared _thisInstance As MySingleton
Protected Sub New()
'initialization code goes here
End Sub
End Class
This prevents .NET from creating a default Public constructor and also prevents any caller from creating their own instance of the object. However, by using Protected, the class can still be subclassed if you need to do this.
Lastly, we want to create a Shared accessor method for the class. It would look like this:
Public Class MySingleton
Private Shared _thisInstance As MySingleton
Protected Sub New()
'initialization code goes here
End Sub
Public Shared Function GetSingleton() As MySingleton
'
' initialize object if it hasn't already been done
'
If _thisInstance Is Nothing Then
_thisInstance = New MySingleton
End If
'
' return the initialized instance
'
Return _thisInstance
End Function
End Class
Since this ‘get’ routine is Shared it can be called without having an instance of the object, as seen here.
Dim TestStuff As MySingleton = MySingleton.GetSingleton
Dim MoreStuff As MySingleton = MySingleton.GetSingleton
In this example both TestStuff and MoreStuff are the same object instance, just with different names. That concept seems a bit confusing at first but don’t let that throw you. Just keep in mind that no matter how many variables you use to hold an instance of your Singleton class, it is actually always the same instance.
When Should You Use the Singleton Pattern?
A common place to use this pattern is with forms. You can easily make a form a Singleton class by following the steps above. The only thing you might want to override the OnClosing event to keep the form open but hidden, like so:
Protected Overrides Sub OnClosing(ByVal e As System.ComponentModel.CancelEventArgs)
e.Cancel = True
Me.Hide()
End Sub
This technique with forms allows you to insure that only a single instance of a particular form is open at once. This is handy for search forms, certain kinds of pop-ups, and other forms commonly used throughout the application.
Using a Singleton is effective in situations where you need to prevent multiple copies of an object. For example, if you had a device driver for a device that only allowed a single connection at a time, a Singleton would be a good solution.
Another use would be in situations where you wanted to cloak the actual instantiation process or otherwise limit usage of a component. I’ve seen this used for proprietary classes and controls in add-on tools where the programmer wanted to hide initialization details from purchasers or limit trial period users.
There are also some drawbacks to using Singletons as well.
First, they can be a pain for other developers to work with. They’re expecting to use a New call but instead have to use a custom initializer. This can be annoying if there isn’t a good and well documented reason behind it. So, always consider your fellow developers when you implement the Singleton pattern.
Another thing to consider is that you may lose some flexibility in how and when the internal object is created based on how the program runs. This is particularly important in classes and components you provide beyond your own applications. Once again, consider other developers.
Using this pattern can lead to some of the problems associated with global variables. Like Globals, it can make a program harder to debug since state changes can come from anywhere. Because of this, I recommend avoiding putting stateful variables in these classes. Also, simply replacing your global variables with a Singleton class isn’t considered a good design practice. It only gives you the illusion of being object oriented. Avoid using this pattern this way.
I hope this introductory overview has been helpful to you. If you have any thoughts or questions on this topic, please feel free to leave me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 13th, 2007
Programmers moving from VB6 into VB.NET and more object oriented programming in general often wonder about how to use the constructor method (New) and the destructor method (Finalize). I’ve seen questions on this topic come up from time to time in VB related discussion forums. Constructors/Destructors of a sort were in VB6, using the names Initialize and Terminate. However, they could not take parameters and were otherwise limited in what they could do so few programmers used them. In VB.NET, the constructors and destructors are much more powerful. In this article, we’ll look at some ways to use them.
New - The Constructor
In OOP a constructor is a function that has the task of initializing the object. In VB.NET, this is defined as a Sub with the name of New. It’s called whenever an object is created using the New statement, like so:
Dim MyProduct As New Product
This isn’t that different from initializing an object in VB6. However, in .NET we can pass data to the constructor so we could have something like this:
Dim MyProduct As New Product(ProductDataRow)
In our first example, the initialization is handled entirely within the object. It might load default variables internally or just do nothing at all. It might look something like this
Public Sub New()
_iD = 0
_description = String.Empty
'other variable init goes here
End Sub
If you don’t include a constructor routine in your code, the VB.NET compiler creates one like this for you, except that it doesn’t contain any variable initialization code. However, if you have a New routine with a parameter in your class, no default empty New routine will be created. In that case, you’ll need to create an empty one yourself. Having this no argument constructor is important when you want to use inheritance so it’s usually a good idea to code one. If you don’t want it to be called other than by an inheriting object, use the Protected access modifier for it.
In our second example, we’re send in a DataRow to initialize the object so the code would look something like this:
Public Sub New(ByVal productData As DataRow)
_iD = CInt(productData.Item("ProductID"))
_description = productData.Item("Description").ToString
'other variable init goes here
End Sub
Now, what really can throw some VB6′ers at first is that both these functions can be in the same class so that you can have multiple constructors in a class. As long as each version of the New routine has a different set of parameters, you can define as many constructors as you want or need. I’ve generally found 6-8 to be a good maximum number of overloaded New methods. Much more than that could indicate design problems although there can be exceptional cases.
As I noted above, you can also control how and when an object is created by using access modifiers. For example, you could make one New method public, such as passing in a datarow, and another private, such as passing in a private object unique to your program as seen here:
Public Sub New()
'Does Nothing
End Sub
Public Sub New(ByVal productData As DataRow)
'Intializes object from datarow
End Sub
Private Sub New(ByVal productData As DataRow, ByVal secretStuff As SecretSauce)
'Intializes object from datarow plus 'SecretSauce' object
End Sub
You can also use private constructors to create a Singleton class pattern by preventing a caller from directly instantiating an object. I’ll be discussing this pattern in an upcoming article.
One thing to keep in mind with your New routines is to keep the code as short as possible here. Lengthy initializations may indicate a design problem with your class, such as it being too complex or bloated. Avoid the temptation of doing everything with this routine. Instead, create additional methods for functionality beyond the basic initialization.
Finalize - The Destructor
The destructor is the last method run by a class. This is called Finalize in VB.NET and it’s called whenever the .NET runtime is told directly or otherwise determines that an object is no longer required. Common uses for the Finalize method is to decrement or increment counters or release resources.
The VB.NET compiler creates a default Finalize method that, behind the scenes, will release variables. However, there may be cases where it’s preferable to have your own code in this routine. When you do this, you have to use the Overrides keyword in your declaration, like so:
Protected Overrides Sub Finalize()
Note that you can’t add parameters to this routine and you can’t use a difference access level than Protected.
One thing to bear in with with Finalize is that there is no set time when this method will be called. Garbage collection, the final release of objects in memory, in the .NET Framework is nondeterministic. Therefore, you should use care when releasing unmanaged objects or calling other classes or components from this routine. You might get unexpected results.
Like the New method, you should also limit the amount of code you put in this routine. My take on it is to simply use the default, compiler built, Finalize unless there is a strong need to perform an action when the object is being destroyed.
I hope this introduction to the way VB.NET uses constructors and destructors has been helpful to you, particularly if you’re migrating to VB.NET from VB6. Let me know what you think or if you have any questions by leaving a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 12th, 2007
XML is perhaps the most common way to store date these days outside of a database. Often, you’ll have the job of searching XML files and strings for particular data. I’ve seen several programmers struggle with this and come up with bad hacks to do the job. XPath, or XML Path Language, provides a way to search XML documents. Unfortunately, it is a bit daunting at first. When looking at the XPath specs it is easy for a novice to get lost. In this article, I’ll try to simplify it somewhat and give some common examples of how to use it.
The Basic VB.NET Code
While there are several .NET objects related to XPath, we will concentrate on the two easiest ones to deal with in this article. In many cases, they’re all you need. These functions are SelectSingleNode and SelectNodes. These functions are part of the XmlNode class, the class that many of the XML classes are derived from. SelectSingleNode returns the first matching node in the target document or fragment while SelectNodes returns a collection of matching nodes.
Just Like a Directory Tree
Anyone who does programming should be familiar with a directory tree, the way directories and sub-directories are organized on a disk drive. As you might guess from the name, XPath works the same way within the framework of the XML Document. The main difference is that we use a forward slash, ‘/’ rather than a back slash, ‘\’, to describe the path. For example, if we look at this snippet of an XML document, how would we describe the path to the product ID?
<products>
<product>
<idcode>00872624</idcode>
</product>
</products>
Here’s what our code would look like:
Dim IdCode as String = ProductXml.SelectSingleNode("/products/product/idcode").InnerText
.
.
.
As you can see, the path is exactly as you would expect “/products/product/idcode”. It walks down the tree to our target value.
Note that we’re using InnerText and not Value to get the value of the element. The Value returns Nothing in most cases so watch out how you code it.
Multiple Values
The example above worked OK if we had a single value, but what if our XML fragment looked like this:
<products>
<product>
<idcode>00872624</idcode>
<category>12</idcode>
</product>
<product>
<idcode>00872845</idcode>
<category>17</idcode>
</product>
<product>
<idcode>01871024</idcode>
<category>12</idcode>
</product>
</products>
What would our results look like? How would we work with it? Here’s our code:
Dim ProductIDList As XmlNodeList = ProductXml.SelectNodes("/products/product/idcode")
For Each ProductNode As XmlNode In ProductIDList
LoadProduct(ProductNode.InnerText)
Next
In this example we load the nodes that we want into the list using the SelectNodes function and then work with the individual nodes.
Filters
OK, it seems pretty easy if we’re dealing with an absolute path but how can we filter the values? Let’s assume that given the fragment above, we only want to get the idcode nodes from products with a category value of 12. How would we describe this XPath and how would we code it?
Filter patterns in XPath are enclosed in square brackets: [value]. So to describe our XPath to our desired category our XPath should look like this: “/products/product[category=12]/idcode”. What this tells XPath is to look at the product nodes and only select those that have a category value of 12.
What if we wanted to exclude products with a category of 12 instead? In that case we would use the inequality operator, !=, like so: “/products/product[category!=12]/idcode”. Note that this is the C style operator, not the VB style <> one.
If you wanted to include both category 12 and 15, what would this look like? It would look like this: “/products/product[category=12 or category=15]/idcode”. Note that there are more complex queries that can be done using XSL and union operations but I won’t be covering them in this article.
Wildcards
There are also wildcard operators in XPath: the asterisk ‘*’, the single and double slash ‘/’ ‘//’, and period and double period ‘.’ ‘..’.
The asterisk ‘*’ tells the XPath operation to select all of the elements regardless of name. For example, If you wanted to find a particular product in our example and return all of the node for the product, the XPath would look like this: “/products/product[idcode=01871024]/*”.
The single slash ‘/’ tells XPath to select immediate children, nodes that are one level below the root node, while the double slash ‘//’ recursively searches through all of the child nodes. This operator can be effective when you need to extract information in depth or just gather it from the top level.
The single period indicates the current, top level, node while the double period indicates the parent of the current node. This can be handy when you’re navigating the tree. For example, if you had selected a particular node and wanted to read an attribute from its parent node.
Attributes
So far, I’ve only dealt with XML that has elements with no attributes. But how does XPath work with attributes as seen in this example?
<products>
<product source='ABC Corp' >
<idcode>00872624</idcode>
<category>12</idcode>
</product>
<product source='XYZ Inc.' >
<idcode>00872845</idcode>
<category>17</idcode>
</product>
<product source='ABC Corp' >
<idcode>01871024</idcode>
<category>12</idcode>
</product>
</products>
The @ symbol identifies part of the path as an attribute rather than an element. Let’s say that we need to get all of the idcodes for products where the source is ABC Corp, what would the XPath look like? It would look like this: “/products/product[@source=’ABC Corp’]/idcode”. Make sure that you remember the quotes around the attribute.
That’s all for this introduction to using XPath. There is a lot more to learn about it so I’ll probably do so more articles on it at some point. Let me know what you would like to see more about or let me know if you have any questions or observations on XPath by leaving me a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 11th, 2007
In a previous article I covered how to use the Action and Predicate Delegates. In this one we will be going over the fundamentals of using the Converter delegate.
The Basics
This delegate is used to convert the members of a generic list from one type to another type. This technique can be very powerful when you need to do this kind of conversion since it handles all of the looping logic internally, making it much faster than a standard for…each loop.
Like the Action and Predicate delegates, the Converter delegate must be written in a specific way. A valid Converter is a function that has one parameter of the source type and a return value of the target type. Here are a few example functions declarations:
Public Function BuildMail(ByVal customerInfo As Customer) As Net.Mail.MailMessage
..........
Public Function GetEmailAddresses(ByVal mail As Net.Mail.MailMessage) As String
..........
Public Function BuildIDList(ByVal key As Guid) As Integer
Note that you can’t add parameters to this declaration. If you have additional values you would like to use in this routine you will need to have a module level variable or, if this is being done on a form, a control value.
Also, the ConvertAll method which uses this delegate is only available for the generic List and the untyped Array. It isn’t available for other generic types.
An Example
In this example we will be using the Converter delegate to convert a list of customer objects into a list of Net.Mail.MailMessage objects. We will assume that our customer objects have all of the required information and are stored in a module level list variable (’_customers’) and that our email message template is determined by a combobox selection on a form.
Here’s the code called from a button’s click event that will call the delegate, first broken down into individual commands and afterward as a single line call:
Private Sub btnCreateEmails_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreateEmails.Click
Dim EmailsToSend As List(Of Net.Mail.MailMessage)
EmailsToSend = customers.ConvertAll(Of Net.Mail.MailMessage)(AddressOf BuildMail)
SendEmails(EmailsToSend)
End Sub
......
Private Sub btnCreateEmails_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCreateEmails.Click
SendEmails(New List(Of Net.Mail.MailMessage) = _customers.ConvertAll(Of Net.Mail.MailMessage)(AddressOf BuildMail))
End Sub
You could write this function either way. I just wanted to show both.
Next, let’s take a look at the Converter delegate itself:
Public Function BuildMail(ByVal customerInfo As Customer) As Net.Mail.MailMessage
Dim Email As New Net.Mail.MailMessage
With Email
.From = txtFromAddress.Text
.To = customerInfo.EmailAddress
.Subject = GetEmailSubject(cboEmailType.SelectedItem)
.Body = GetEmailBody(cboEmailType.SelectedItem, customerInfo)
End With
Return Email
End Function
In this function we see that an object from the original list is passed in and values are read from it to build the corresponding object for the new list. Also, we see values from controls being used to construct the new object as well.
I hope this description and example have given you some help in understanding the Converter delegate. Let me know if you have anything to add or if you have any questions by leaving a comment.
Share This Article:
These icons link to social bookmarking sites where readers can share and discover new web pages.
September 10th, 2007
Next Posts
Previous Posts