views:

120

answers:

2

Hello All, I have the following classes:

Public Class Email

    Private Shared ReadOnly EMAIL_REGEX = "\b[a-zA-Z]+[a-zA-Z0-9._+-]+@" + _
                                      "[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}\b"
    Private _email As String

    Public Event emailCreated()

    ' Declare empty constructor private so the only way to create an object
    ' is using new (email)
    Private Sub New()

    End Sub

    Sub New(ByVal email As String)
        If Regex.IsMatch(email, EMAIL_REGEX) Then
            _email = email
            RaiseEvent emailCreated()
        Else
            Throw New Exception("Email Not Valid")
        End If
    End Sub

    ReadOnly Property Email() As String
        Get
            Return _email
        End Get
    End Property

End Class

And

Public Class EmailForm

    WithEvents myEmail As Email

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Try
            myEmail = New Email(TextBox1.Text)
        Catch ex As Exception
            MessageBox.Show("Exception: " & ex.Message)
        End Try
    End Sub

    Public Sub emailCreated() Handles myEmail.emailCreated
        MessageBox.Show("New Email Created")
    End Sub

End Class

If a create a wrong email lets say "email" the exception is correctly cached and a message is showed however is i input a valid email the event is not raised, the object is being created but no message is shown and no error or exception is thrown i suspect it has something to do with using "myemail = new Email(email)" but i have seen examples of using new with withevents with no problem. I would apreciate any input about this problem

thank you

Edit: for future reference - http://stackoverflow.com/questions/1624496/vb-net-problem-with-members-event-handling

+1  A: 

First guess: an Email is created before events are hooked up to it. You're firing the event before you're even out of the constructor, before VB even has the opportunity to add event handlers, so the event will never be seen.

Bigger-picture view. Is there a particular reason you want an emailCreated event fired? It's looking like the event will never be handled outside your own class even if you end up getting it working, so using events adds complexity you may not need or even want.

cHao
@cHao Hello Thank you for your answer, the idea is to make the constructor the only way to create a valid email if i create a setter then someone could use the same email object multiple times if there a way to avoid that? maybe making the _email readonly.
Hei
You don't need events for that -- in fact, that's probably the oddest use yet that i've seen for them. What's wrong with just having the properties read-only, and setting everything you need in the constructor, and throwing an exception if something goes wrong? Then if you have an Email, you know it's valid.
cHao
That would be the ideal situation, I just wanted to learn more about events. But yes I agree with you
Hei
Ahh. Good reason. :) For reference, events are best used to notify about interesting stuff that happens after the object's created (say, when a property of the object changes, which is the most common case i've seen). If you really care about the email being created, that should probably be an event on the class that creates it...but unless someone should care that an Email was created, the event probably shouldn't be there.
cHao
Good Advice, I am going to follow SLaks suggestion and create a property, and raise a event Email_Changed there, I am not used to work with events it seems to me like they break the normal flow of the program I can see how that can become messy if i use lots of them.
Hei
+1  A: 

You should not validate email addresses using regular expressions.

You should use the MailAddress class instead:

Sub New(ByVal email As String)
    New MailAddress(email).ToString()
End Sub

The MailAddress class uses a BNF parser to validate the address, and will throw a FormatException if the address is invalid.

Your regex will reject valid addresses like [email protected].


To answer your question, the event is being raised before the WithEvents field has a value.

Here is what happens when your code is executed:

  1. New Email(TextBox1.Text) – The Email object is created, and the constructor raises the event.
    At this point, the event has no handlers, so the event doesn't do anything.
  2. myEmail = ... The new Email object is assigned to the myEmail field, and the event handler is added.

It is not possible to work around this problem, because you cannot get a reference to an object before its constructor finishes.

Instead, you should get rid of the event and make the myEmail field into a property with your code in the property setter

SLaks
Thank you for your answer I was not aware of the MailAddress class however this does not solve the event problem.
Hei
I understand now, basically the event is being raised before it can be handled, the obvious solution seems to be raising the event outside the constructor, is there a way to do this without using a 2 step process like "e = new email() e.Email = "[email protected]" " ?
Hei
Yes, but you shouldn't. It would be much cleaner to use a property instead.
SLaks