Serialization - Pesky VB's Event implementation.

T

Trev Hunter

Good point. I'll see about modifying the article.

Cheers Jay.

Trev.

Jay B. Harlow said:
Trev,
I'm not so sure that the NonSerialized attribute needs to be fixed as much
as VB.NET needs to support the Field modifier on attributes. Other wise it
looks like a good article...

In C# you use:
[field: NonSerialized]
public event EventHandler NameChanged;

Unfortunately VB.NET only supports Assembly & Module modifiers on attributes

Such as:
<Assembly: NonSerialized>
or
<Module: NonSerialized>

The Field modifier is not supported.

<Field: NonSerialized> causes a syntax error.

Remember the thread from last October?

Hope this helps
Jay

Trev Hunter said:
Ok,

I've decided to channel my monkey-rambling energy into providing a
(hopefully) useful article on CodeProject about serialization of VB classes
with events.

Have a look at http://www.codeproject.com/useritems/serializevbclasses.asp

and let me know what you think

Best Regards,

Trev.
VB.
A
simple task you may think - stick the <Serialized()> attribute on the class
and away you go. As Homer would say - "D'Oh"

The root of my problem lies in the way VB implements Events and the fact
that you can't apply the <NonSerialized> attribute to the little rascals.
The result of this is that when you serialize ObjectA that has an event
EventX being handled by a method in ObjectB, ObjectB gets serialized right
along with ObjectA.

If you don't believe me, go ahead and try it. Create a serializable class
with an event and some private fields. Create a form an declare an instance
of the class and handle the event (with withevents or addhandler - doesn't
matter). Now try and serialize the instance of the class - you'll get a
serialization exception because the form (not the class you're trying to
serialize) isn't serializable!

Fair enough, you may say. This shouldn't be a big problem - just remove
event handlers before serializing. Why should I have to do this? I can't
even do it reliably (because I have no way of telling who has attached
to
my
events - it could be some client that I have no control over).

Another problem becomes apparent if you have a custom collection of objects
(say Trigger objects that fire an event periodically). The Collection of
triggers has it's own event which is raised when any of the triggers it
contains fires. A simple (but admittedly probably not the best) way to
implement the collection would be to use addhandler to attach to a trigger's
event when adding it to the collection. Now, guess what happens when
you
try
and serialize the collection - it works! WooHoo!. Now try and deserialize
it. Again "D'Oh". This time you get an exception because you can't
deserialize a delegate to a private method for security reasons!

In my travels I've had many suggestions about how to get around this and
have come up with some solutions on my own. Here are a few of them and their
pros and cons:

1) Implement ISerializable and don't serialize the events.
Pros: It Works
Cons: Too much work - have to change the GetObjectData method and
Constructor every time I add a field that is to be serialized.


2) Implement an ISerializationSurrogate to strip out the events:
Pros: Dunno
Cons: Pretty complicated to do because the Binary Formatter and Soap
Formatter don't allow you to specify surrogates, so you end up re-writing
them too.

3) Implement ISerializable in a base class and use reflection to get all
private fields in derived classes
Pros: It works
Cons: It's a hack

4) Implement the events in a C# Base class
Pros: You can specify the [NonSerialized] attribute to the event
Cons: Your logic is spread across two projects and two languages.


As you can see. I'm mighty fed up with events. I realize that the fact that
the <NonSerialized> attribute can't be applied to events is probably an
oversight and will probably be fixed in the next version. Saying that, can
anybody think of a valid reason why you would want to serialize an event
(and all objects that handle its events) anyway?


Thanks for putting up with this monkey-ramble. I'm off to learn C# ;)

Cheers,

Trev.
 
J

Jay B. Harlow [MVP - Outlook]

Trev,
Another couple of comments on your article.

<blockquote>
Because of the way VB.NET implements events, when you serialize an object,
its events get serialized too (because events are actually kinda like hidden
multicast delegate fields). A side effect of this is that any object which
handles events raised by the object being serialized will be considered part
of the object graph and will be serialized too.
</blockquote>

Events *are* implemented with hidden multicast delegate fields! (no kinda
about it)

Look at the following class with ILDASM.

Public Class EventClass

Public Event AnEvent As EventHandler

Public Sub Test()
If AnEventEvent Is Nothing Then
Debug.WriteLine("no", "Any handlers?")
Else
Debug.WriteLine("yes", "Any handlers?")
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "#
handlers")
End If
End Sub

End Class

You should notice the event itself as 'AnEvent',

.event [mscorlib]System.EventHandler AnEvent

However you should also notice two routines add_AnEvent & remove_AnEvent,
these are used by the AddHandler & RemoveHandler statements,

.method public specialname instance void
add_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized
.method public specialname instance void
remove_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized

Plus you should notice the delegate field itself.

.field private class [mscorlib]System.EventHandler AnEventEvent

Which allows you within your code to use all the methods on Delegate to
manipulate the delegate. One I find useful is GetInvocationList to see how
many handlers are attached. Or checking to see if AnEventEvent is Nothing to
see if there is a Handler attached.

<blockquote>
The root of the problem is the fact that you cannot apply the
<NonSerialized> attribute to events or fields in VB
</blockquote>
You and I both know that "fields" above is referring to the underlying field
of the event, I'm not sure if you need qualify it with something like
"events or their fields"?

Hope this helps
Jay
 
T

Trev Hunter

Thanks for your help with this Jay. I'll fix up the article soon, taking
your recommendations into account.
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "# handlers")

Nice one. I didn't know you could access an event delegate field in this way
(because it doesn't show up with intellisense). I guess it's just yet
another thing that VB tries its best to hide from the developer. IMHO, when
Microsoft brought things into the light that were hidden in VB6 (e.g. the
code behind the layout of a windows form), they should have went the whole
way. If they trust us with things like delegates, why go to such lengths to
hide the implementation of events?

Cheers again,

Trev.

Jay B. Harlow said:
Trev,
Another couple of comments on your article.

<blockquote>
Because of the way VB.NET implements events, when you serialize an object,
its events get serialized too (because events are actually kinda like hidden
multicast delegate fields). A side effect of this is that any object which
handles events raised by the object being serialized will be considered part
of the object graph and will be serialized too.
</blockquote>

Events *are* implemented with hidden multicast delegate fields! (no kinda
about it)

Look at the following class with ILDASM.

Public Class EventClass

Public Event AnEvent As EventHandler

Public Sub Test()
If AnEventEvent Is Nothing Then
Debug.WriteLine("no", "Any handlers?")
Else
Debug.WriteLine("yes", "Any handlers?")
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "#
handlers")
End If
End Sub

End Class

You should notice the event itself as 'AnEvent',

.event [mscorlib]System.EventHandler AnEvent

However you should also notice two routines add_AnEvent & remove_AnEvent,
these are used by the AddHandler & RemoveHandler statements,

.method public specialname instance void
add_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized
.method public specialname instance void
remove_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized

Plus you should notice the delegate field itself.

.field private class [mscorlib]System.EventHandler AnEventEvent

Which allows you within your code to use all the methods on Delegate to
manipulate the delegate. One I find useful is GetInvocationList to see how
many handlers are attached. Or checking to see if AnEventEvent is Nothing to
see if there is a Handler attached.

<blockquote>
The root of the problem is the fact that you cannot apply the
<NonSerialized> attribute to events or fields in VB
</blockquote>
You and I both know that "fields" above is referring to the underlying field
of the event, I'm not sure if you need qualify it with something like
"events or their fields"?

Hope this helps
Jay

Trev Hunter said:
Looks like Codeproject edited and moved the article. It now resides at

http://www.codeproject.com/vb/net/serializevbclasses.asp

if you're interested.

Trev.
 
J

Jay B. Harlow [MVP - Outlook]

Trev,
way. If they trust us with things like delegates, why go to such lengths to
hide the implementation of events?
To protected us from ourselves? ;-)

I think part of the "problem" is you have "most" VB.NET developers who is
Microsoft's "target" audience where the bulk of these details should be
hidden, hence the "Hide advanced members" option in VS.NET (Tools -
Options - Text Editor - Basic - General). As they don't "care" as they just
want to get their GUI done in a RAD fashion. (I'm not sure if I needed my
flame retardant suit on for that one or not ;-))

Then you have the significantly to extremely more advanced VB.NET developers
like you & I...

I'm sure when MS adds features they try to weigh the usefulness of
implementing it & the usefulness exposing it considering their target
audience... At least I hope they would as I know I would. ;-)

Its like an articles I was reading (a Chat I believe). VB.NET Whidbey may
get some Refactoring like features, however they may not call it Refactoring
(like C# Whidbey does) as their target audience may overlook a feature
called Refactoring, as they don't know what it is. Hell I mention
Refactoring in this newsgroup multiple times a month! ;-)

BTW: Refactoring = http://www.refactoring.com

Just a thought
Jay


Trev Hunter said:
Thanks for your help with this Jay. I'll fix up the article soon, taking
your recommendations into account.
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "# handlers")

Nice one. I didn't know you could access an event delegate field in this way
(because it doesn't show up with intellisense). I guess it's just yet
another thing that VB tries its best to hide from the developer. IMHO, when
Microsoft brought things into the light that were hidden in VB6 (e.g. the
code behind the layout of a windows form), they should have went the whole
way. If they trust us with things like delegates, why go to such lengths to
hide the implementation of events?

Cheers again,

Trev.

Jay B. Harlow said:
Trev,
Another couple of comments on your article.

<blockquote>
Because of the way VB.NET implements events, when you serialize an object,
its events get serialized too (because events are actually kinda like hidden
multicast delegate fields). A side effect of this is that any object which
handles events raised by the object being serialized will be considered part
of the object graph and will be serialized too.
</blockquote>

Events *are* implemented with hidden multicast delegate fields! (no kinda
about it)

Look at the following class with ILDASM.

Public Class EventClass

Public Event AnEvent As EventHandler

Public Sub Test()
If AnEventEvent Is Nothing Then
Debug.WriteLine("no", "Any handlers?")
Else
Debug.WriteLine("yes", "Any handlers?")
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "#
handlers")
End If
End Sub

End Class

You should notice the event itself as 'AnEvent',

.event [mscorlib]System.EventHandler AnEvent

However you should also notice two routines add_AnEvent & remove_AnEvent,
these are used by the AddHandler & RemoveHandler statements,

.method public specialname instance void
add_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized
.method public specialname instance void
remove_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized

Plus you should notice the delegate field itself.

.field private class [mscorlib]System.EventHandler AnEventEvent

Which allows you within your code to use all the methods on Delegate to
manipulate the delegate. One I find useful is GetInvocationList to see how
many handlers are attached. Or checking to see if AnEventEvent is
Nothing
to
see if there is a Handler attached.

<blockquote>
The root of the problem is the fact that you cannot apply the
<NonSerialized> attribute to events or fields in VB
</blockquote>
You and I both know that "fields" above is referring to the underlying field
of the event, I'm not sure if you need qualify it with something like
"events or their fields"?

Hope this helps
Jay
 
T

Trev Hunter

(I'm not sure if I needed my flame
retardant suit on for that one or not ;-))

Lol !
I'm sure when MS adds features they try to
weigh the usefulness of
implementing it & the usefulness
exposing it considering their target
audience... At least I hope they would as
I know I would. ;-)

I don't propose that I know anything about how the IDE is implemented, but I
would have thought that hiding the event field is a feature they would have
had to explicitly put into Visual Studio, not vise versa. As a side note on
weighing up the usefulness of something, I guess they missed the boat when
it came to those damn animated help assistants in Office and the like ;)

I totally agree with you on the target audience stuff (I've been known to
write applications for users that have trouble typing in numbers when the
numlock key gets un-selected ;), but at the same time, I think with the
power that .net brings to the world of VB (e.g. threading, remoting etc.
etc.) it's time that Microsoft considered that there may a good few more
"advanced" users out there (not that there was any shortage under VB6).
Didn't I hear somewhere that "all .net languages were created equal"? Guess
it's just a case of "some are more equal than others" ;)
Hell I mention
Refactoring in this
newsgroup multiple times a month! ;-)

*gets dictionary out, looks up link, saves in favourites for a closer look*

Seriously, I'll have to take a look at that soon. For those (like me) who
didn't know what refactoring is:

http://www.google.com/search?q=define:+Refactoring


Cheers again for your thoughts,

Trev.



Jay B. Harlow said:
Trev,
way. If they trust us with things like delegates, why go to such lengths to
hide the implementation of events?
To protected us from ourselves? ;-)

I think part of the "problem" is you have "most" VB.NET developers who is
Microsoft's "target" audience where the bulk of these details should be
hidden, hence the "Hide advanced members" option in VS.NET (Tools -
Options - Text Editor - Basic - General). As they don't "care" as they just
want to get their GUI done in a RAD fashion. (I'm not sure if I needed my
flame retardant suit on for that one or not ;-))

Then you have the significantly to extremely more advanced VB.NET developers
like you & I...

I'm sure when MS adds features they try to weigh the usefulness of
implementing it & the usefulness exposing it considering their target
audience... At least I hope they would as I know I would. ;-)

Its like an articles I was reading (a Chat I believe). VB.NET Whidbey may
get some Refactoring like features, however they may not call it Refactoring
(like C# Whidbey does) as their target audience may overlook a feature
called Refactoring, as they don't know what it is. Hell I mention
Refactoring in this newsgroup multiple times a month! ;-)

BTW: Refactoring = http://www.refactoring.com

Just a thought
Jay


Trev Hunter said:
Thanks for your help with this Jay. I'll fix up the article soon, taking
your recommendations into account.
Debug.WriteLine(AnEventEvent.GetInvocationList().Length, "# handlers")

Nice one. I didn't know you could access an event delegate field in this way
(because it doesn't show up with intellisense). I guess it's just yet
another thing that VB tries its best to hide from the developer. IMHO, when
Microsoft brought things into the light that were hidden in VB6 (e.g. the
code behind the layout of a windows form), they should have went the whole
way. If they trust us with things like delegates, why go to such lengths to
hide the implementation of events?

Cheers again,

Trev.

Trev,
Another couple of comments on your article.

<blockquote>
Because of the way VB.NET implements events, when you serialize an object,
its events get serialized too (because events are actually kinda like hidden
multicast delegate fields). A side effect of this is that any object which
handles events raised by the object being serialized will be
considered
part
of the object graph and will be serialized too.
</blockquote>

Events *are* implemented with hidden multicast delegate fields! (no kinda
about it)

Look at the following class with ILDASM.

Public Class EventClass

Public Event AnEvent As EventHandler

Public Sub Test()
If AnEventEvent Is Nothing Then
Debug.WriteLine("no", "Any handlers?")
Else
Debug.WriteLine("yes", "Any handlers?")
Debug.WriteLine(AnEventEvent.GetInvocationList().Length,
"#
handlers")
End If
End Sub

End Class

You should notice the event itself as 'AnEvent',

.event [mscorlib]System.EventHandler AnEvent

However you should also notice two routines add_AnEvent & remove_AnEvent,
these are used by the AddHandler & RemoveHandler statements,

.method public specialname instance void
add_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized
.method public specialname instance void
remove_AnEvent(class [mscorlib]System.EventHandler obj) cil managed
synchronized

Plus you should notice the delegate field itself.

.field private class [mscorlib]System.EventHandler AnEventEvent

Which allows you within your code to use all the methods on Delegate to
manipulate the delegate. One I find useful is GetInvocationList to see how
many handlers are attached. Or checking to see if AnEventEvent is
Nothing
to
see if there is a Handler attached.

<blockquote>
The root of the problem is the fact that you cannot apply the
<NonSerialized> attribute to events or fields in VB
</blockquote>
You and I both know that "fields" above is referring to the underlying field
of the event, I'm not sure if you need qualify it with something like
"events or their fields"?

Hope this helps
Jay

Looks like Codeproject edited and moved the article. It now resides at

http://www.codeproject.com/vb/net/serializevbclasses.asp

if you're interested.

Trev.
 
J

Jay B. Harlow [MVP - Outlook]

Trev,
I don't propose that I know anything about how the IDE is implemented, but I
would have thought that hiding the event field is a feature they would have
had to explicitly put into Visual Studio, not vise versa
I view the hiding the Event an artifact of how VB.NET is implemented on top
of IL & the CLR, not so much how VB.NET is integrated with VS.NET. Which is
why they are "hidden" even from the "hide advanced members".
Didn't I hear somewhere that "all .net languages were created equal"? Guess
it's just a case of "some are more equal than others" ;)
IMHO they are "created equal" by virtue they are all built on top of IL &
the CLR, however they are not equal, as how each language exposes an IL/CLR
feature may be different, if they expose it.

For example C# supports defining the add/remove procedures for Events
(similar to the get/set for properties) while VB.NET handles (no pun
intended) it completely for you. Or that VB.NET supports the WithEvents &
Handles keyword (which causes the WithEvents field to really be an IL
property) while C# requires you to code that logic yourself...

Hope this helps
Jay

Trev Hunter said:
Lol !


I don't propose that I know anything about how the IDE is implemented, but I
would have thought that hiding the event field is a feature they would have
had to explicitly put into Visual Studio, not vise versa. As a side note on
weighing up the usefulness of something, I guess they missed the boat when
it came to those damn animated help assistants in Office and the like ;)

I totally agree with you on the target audience stuff (I've been known to
write applications for users that have trouble typing in numbers when the
numlock key gets un-selected ;), but at the same time, I think with the
power that .net brings to the world of VB (e.g. threading, remoting etc.
etc.) it's time that Microsoft considered that there may a good few more
"advanced" users out there (not that there was any shortage under VB6).
Didn't I hear somewhere that "all .net languages were created equal"? Guess
it's just a case of "some are more equal than others" ;)


*gets dictionary out, looks up link, saves in favourites for a closer look*

Seriously, I'll have to take a look at that soon. For those (like me) who
didn't know what refactoring is:

http://www.google.com/search?q=define:+Refactoring


Cheers again for your thoughts,

Trev.
<<snip>>
 
T

Trev Hunter

I view the hiding the Event an artifact
of how VB.NET is implemented on top
of IL & the CLR, not so much how VB.NET
is integrated with VS.NET. Which is
why they are "hidden" even from the
"hide advanced members".

I see where you're coming from, but IMLB the hiding of the event delegate
field would be done somewhere in the IDE - it is visible in IL, and even
appears with intellisense once you type the name for yourself (could this be
one of those "back-doors" left over from development and testing of the
ide?)

No big deal anyway. Just rolling things over in my head. Many thanks again
for all your help. I should have a new and improved version of the article
(and source code) by the end of the week if you're still interested.

Thanks again,

Trev.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top