Multicast delegates and hashtable problem

R

raylopez99

Hello all—

I’m trying to get the below to work and cannot get the format right.

It’s from this example: http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

What it is: I’m trying to store multicast delegates in a hash table,
and then fire the delegates one of two ways (after registering/
creating the delegates, etc).

First, either traversing the entire hash table, using foreach
(Hashtable h in eventTable) logic (see- //does not work-how to make it
work?).

Second, by calling up the value of a hashtable through the key. But
how to cast and fire the delegate? I get the error CS0079, see below.

But manually I can get these delegates to fire (see output at Main
below). However, I want to automate this process (I think that’s the
point behind the link above, though I could be wrong).

Any suggestions even outside of actual code appreciated.

RL

/////////////// works, but not ‘automated’
hi:1
hi:10
Ray
i, object are: 123, !
i, object are: 123, $
i, object areTWO: 123, $
notice chaining of delegates for x3
myFoo4()...nothing
The End
Press any key to continue . . .

///////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MediatorC
{
class Program
{
static void Main(string[] args)
{
// http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

PropertyEventsSample myPES = new PropertyEventsSample();

// manual way - works but not automated

MyDelegate1 x = null;

// x += new MyDelegate1(myPES.myTest1.myFoo1); //old style
x += myPES.myTest1.myFoo1;

x(1); //works
x += new MyDelegate1(myPES.myTest1.myFoo12);
x(10); //works

MyDelegate2 x2 = null;
x2 += new MyDelegate2(myPES.myTest1.myFoo2);
x2("Ray");

MyDelegate3 x3 = null;
x3 += new MyDelegate3(myPES.myTest1.myFoo3);
x3(123, '!');
x3 += new MyDelegate3(myPES.myTest1.myFoo32);
x3(123, '$');

Console.WriteLine("notice chaining of delegates for x3");

MyDelegate4 x4 = null;
x4 += new MyDelegate4(myPES.myTest1.myFoo4);
x4();


// trying to get this to fire...failed
//myPES.Event1 += new MyDelegate1(myPES.myTest1.myFoo1);
//myPES.Event2 += new MyDelegate1(myPES.myTest1.myFoo12);
////
//myPES.Event3 += new MyDelegate2(myPES.myTest1.myFoo2);
////
//myPES.Event4 += new MyDelegate3(myPES.myTest1.myFoo3);
//myPES.Event5 += new MyDelegate3(myPES.myTest1.myFoo32);
////
//myPES.Event6 += new MyDelegate4(myPES.myTest1.myFoo4);



//////////////////////////// failed///
// myPES.FireAway("Event1");

// myPES.Event1(1);

// myPES.Event1.Invoke(1);

// myPES.eventTable["Event1"]
Console.WriteLine("The End");


}
}
}
//////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MediatorC
{

public delegate void MyDelegate1(int i);
public delegate void MyDelegate2(string s);
public delegate void MyDelegate3(int i, object o);
public delegate void MyDelegate4();

public class Test1
{

public int i;
public string s;
public Test1()
{ i = 0; s = "hi!"; }

public void myFoo1(int i)
{ this.i = i; Console.WriteLine("hi:{0}", i); }

public void myFoo12(int i)
{ this.i = 2*i; }

public void myFoo2(string s)
{ this.s = s; Console.WriteLine(s); }

public void myFoo3(int i, object o)
{ Console.WriteLine("i, object are: {0}, {1}", i, o); }

public void myFoo32(int i, object o)
{ Console.WriteLine("i, object areTWO: {0}, {1}", i, o); }

public void myFoo4()
{ Console.WriteLine("myFoo4()...nothing"); }


}

public class PropertyEventsSample
{
//http://msdn.microsoft.com/en-us/library/8627sbea(VS.
71).aspx
public Test1 myTest1;
private Hashtable eventTable;

public PropertyEventsSample()
{
myTest1 = new Test1();

eventTable = new Hashtable();

Event1 += new MyDelegate1(myTest1.myFoo1);
// eventTable.Add("Event1", Event1); //improper, does not work

//eventTable.Add("Event1", Event1); //improper, does not work
//error CS0079 The event
'MediatorC.PropertyEventsSample.Event1' can only appear on the left
hand side of += or -=

Event2 += new MyDelegate1(myTest1.myFoo12);
//
Event3 += new MyDelegate2(myTest1.myFoo2);
//
Event4 += new MyDelegate3(myTest1.myFoo3);
Event5 += new MyDelegate3(myTest1.myFoo32);
//
Event6 += new MyDelegate4(myTest1.myFoo4);

}

public void FireAway()
{
//if (eventTable[str1] != null)
//{ }
foreach (Hashtable h in eventTable)
{
//h.Values; //does not work-how to make it work?
}
//
}

public event MyDelegate1 Event1
{
add
{
eventTable["Event1"] =
(MyDelegate1)eventTable["Event1"] + value;
}
remove
{
eventTable["Event1"] =
(MyDelegate1)eventTable["Event1"] - value;
}
}

public event MyDelegate1 Event2
{
add
{
eventTable["Event2"] =
(MyDelegate1)eventTable["Event2"] + value;
}
remove
{
eventTable["Event2"] =
(MyDelegate1)eventTable["Event2"] - value;
}
}

public event MyDelegate2 Event3
{
add
{
eventTable["Event3"] =
(MyDelegate2)eventTable["Event3"] + value;
}
remove
{
eventTable["Event3"] =
(MyDelegate2)eventTable["Event3"] - value;
}
}

public event MyDelegate3 Event4
{
add
{
eventTable["Event4"] =
(MyDelegate3)eventTable["Event4"] + value;
}
remove
{
eventTable["Event4"] =
(MyDelegate3)eventTable["Event4"] - value;
}
}

public event MyDelegate3 Event5
{
add
{
eventTable["Event5"] =
(MyDelegate3)eventTable["Event5"] + value;
}
remove
{
eventTable["Event5"] =
(MyDelegate3)eventTable["Event5"] - value;
}
}

public event MyDelegate4 Event6
{
add
{
eventTable["Event6"] =
(MyDelegate4)eventTable["Event6"] + value;
}
remove
{
eventTable["Event6"] =
(MyDelegate4)eventTable["Event6"] - value;
}
}
}
}
///////////////////////////////////////
 
J

Jon Skeet [C# MVP]

raylopez99 said:
I=3Fm trying to get the below to work and cannot get the format right.

It=3Fs from this example: http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

What it is: I=3Fm trying to store multicast delegates in a hash table,
and then fire the delegates one of two ways (after registering/
creating the delegates, etc).

Why are you using Hashtable instead of the strongly typed
Dictionary said:
First, either traversing the entire hash table, using foreach
(Hashtable h in eventTable) logic (see- //does not work-how to make it
work?).

The hashtable isn't storing hashtables, it's storing key/value pairs
where the key is a string and the value is a delegate. It's not exactly
clear what you're trying to do here.
Second, by calling up the value of a hashtable through the key. But
how to cast and fire the delegate? I get the error CS0079, see below.

You're trying to use an event as if it's a variable. Events just
support add/remove - not fetch. See
http://pobox.com/~skeet/csharp/events.html
But manually I can get these delegates to fire (see output at Main
below). However, I want to automate this process (I think that=3Fs the
point behind the link above, though I could be wrong).

Any suggestions even outside of actual code appreciated.

It's not clear to me which bit is failing for you, partly because your
example is so long. You've commented out lots of code, but lots of that
is okay. *Short* but complete examples are important - although I'm
grateful that the example is at least complete.

If all that's going wrong is going through the hashtable - and if you
really still want to use a hashtable - then you need to cast each value
in the table to the appropriate delegate type and execute it. That's
somewhat tricky in your case, as you've got 4 different signatures -
what do you want the arguments to the delegate invocation to be?
 
R

raylopez99

Why are you using Hashtable instead of the strongly typed
Dictionary<TKey, TValue>?

Because that was the container in: http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

BTW, if you care to comment about what exactly is going on in the
above link, please feel free to do so.
The hashtable isn't storing hashtables, it's storing key/value pairs
where the key is a string and the value is a delegate. It's not exactly
clear what you're trying to do here.

I found a solution that does what I am trying to do--at least the
first of the two questions. If you have the book by O'Reilly "C#
Cookbook" by Hilyard and Teilhet, 2nd ed (Updated for C#2.0), look at
chapter 9, receipe 9.1 "Controlling when and if a delegate fires
within a multicast delegate". They use an array to store various
delegates, rather than a hash table as in my OP, then they traverse
the array using for() logic. They show how you can traverse backwards
and forwards and skip every other delegate, etc, as you can traversing
any array. Basically one of the two questions was this--how to store
disparate delegates in a container and traverse the container. Of
interest is that you can add delegates of disparate signatures,
exactly as I proposed, e.g., for those who don't have the book before
them reading this thread:

//
/ The following two creation expressions are equivalent,
// where InstanceMethod is an instance method in the class
// containing the creation expression (or a base class).
// The target is "this".
FirstDelegate d1 = new FirstDelegate(InstanceMethod);
FirstDelegate d2 = new FirstDelegate(this.InstanceMethod);

// Here we create a delegate instance referring to the same method
// as the first two examples, but with a different target.
FirstDelegate d3 = new FirstDelegate(anotherInstance.InstanceMethod);

// This delegate instance uses an instance method in a different
class,
// specifying the target to call the method on
FirstDelegate d4 = new
FirstDelegate(instanceOfOtherClass.OtherInstanceMethod);

// This delegate instance uses a static method in ths class containing
// the creation expression (or a base class).
FirstDelegate d5 = new FirstDelegate(StaticMethod);

// This delegate instance uses a static method in a different class
FirstDelegate d6 = new FirstDelegate(OtherClass.OtherStaticMethod);

//now add this!:

FirstDelegate SUPERDELEGATE = d1+d2+d3+d4+d5+d6;

//and now:

Delegate[] delegateArrayList = SUPERDELEGATE.GetInvocationList();
for (int counter = delegateArrayList.Length-1; counter >= 0;
counter--)
{
((FirstDelegate) delegateArrayList[counter])();
}

//This should fire the delegates in the order d6, d5, d4, d3, d2, d1.
I haven't tried it yet, but using ex. 9.1 from the Cookbook.

// End
You're trying to use an event as if it's a variable. Events just
support add/remove - not fetch. Seehttp://pobox.com/~skeet/csharp/events.html

Could not follow. Can you give an example of a cast from a hashtable
(or a dictionary)? Referring to the above example for an array, a
cast would be along the lines of: ((FirstDelegate)
delegateArrayList[counter])();

If it's quick to show--don't waste your time please.

If all that's going wrong is going through the hashtable - and if you
really still want to use a hashtable - then you need to cast each value
in the table to the appropriate delegate type and execute it. That's
somewhat tricky in your case, as you've got 4 different signatures -
what do you want the arguments to the delegate invocation to be?

Aha! A valuable clue "somewhat tricky". So perhaps the link by MSDN
above is referring to something else. And perhaps the receipe 9.1 is
the way to go rather than using a hashtable or dictionary...I'll post
later today if I have time with a solution...

Thanks for your reply.

RL
 
J

Jon Skeet [C# MVP]

raylopez99 said:

That's what you get from reading 5-year-old documentation - but it's
always a good idea to use the generic containers instead of
Hashtable/ArrayList/etc.
BTW, if you care to comment about what exactly is going on in the
above link, please feel free to do so.

Which bit are you puzzled about?
I found a solution that does what I am trying to do--at least the
first of the two questions. If you have the book by O'Reilly "C#
Cookbook" by Hilyard and Teilhet, 2nd ed (Updated for C#2.0), look at
chapter 9, receipe 9.1 "Controlling when and if a delegate fires
within a multicast delegate". They use an array to store various
delegates, rather than a hash table as in my OP, then they traverse
the array using for() logic. They show how you can traverse backwards
and forwards and skip every other delegate, etc, as you can traversing
any array. Basically one of the two questions was this--how to store
disparate delegates in a container and traverse the container. Of
interest is that you can add delegates of disparate signatures,
exactly as I proposed, e.g., for those who don't have the book before
them reading this thread:

Having different types within a container usually leads to more issues
than it solves, IMO...
Could not follow.

You were trying to add "Event1" to your hashtable. You can't do that,
because that's an event, not a delegate.
Can you give an example of a cast from a hashtable
(or a dictionary)? Referring to the above example for an array, a
cast would be along the lines of: ((FirstDelegate)
delegateArrayList[counter])();

If it's quick to show--don't waste your time please.

foreach (Delegate delegate in hashtable.Keys)
Aha! A valuable clue "somewhat tricky".

The more valuable clue should be the question of what arguments you
want to pass in.
So perhaps the link by MSDN
above is referring to something else. And perhaps the receipe 9.1 is
the way to go rather than using a hashtable or dictionary...I'll post
later today if I have time with a solution...

Why are you trying to do this anyway? What's the overall goal? There's
almost certainly a better way of doing it.
 
R

raylopez99

That's what you get from reading 5-year-old documentation - but it's
always a good idea to use the generic containers instead of
Hashtable/ArrayList/etc.

OK, you solved it then. Thanks.

Which bit are you puzzled about?

Nothing. It's obsolete, like you say. let's move on, I got some
better stuff (see below).


Having different types within a container usually leads to more issues
than it solves, IMO...

Good. Because I was thinking of using ArrayList (which as you know
can store different types) to store different Delegates (having
different signatures). But you talked me out of it. Now see the
below.


foreach (Delegate delegate in hashtable.Keys)

Exactly. I'm struggling with this and concluding it's not worth the
hassle. Better to have a 'loop' or container for each delegate, see
below for example with MyDelegate3.

Why are you trying to do this anyway? What's the overall goal? There's
almost certainly a better way of doing it.

No overall goal. I'm trying to see how to use delegates. You can get
a delegate to return an int or a parameter than use it to control
whether you fire other delegates "chained" in an array. This is
Receipe 9.1 in the Cookbook.

Now see this--there's many ways of registering/instantiating/setting
up delegates. Note the way I now handle "MyDelegate3" below (I've
commented out the way it was done previously). I'm using an array
this time.

Start with the comment //////// equivalent Jon Skeet start here
This also shows how stuff is equivalent.

Then move to "MyDelegate3" and see how I use an array to fire the two
instances of this delegate. But--and this is key--they are the same
type of delegate (taking an 'int' and an 'object' and returning
'void'). Another key: using the method GetInvocationList (see
below).

I've concluded--thanks to this thread--that trying to fit disparate
delegates into a container and then feeding them parameters, etc, is
not worth the cost. Talk me out of it if you may but this is a rule I
will file away for future reference.

I might post later on using a Hashtable in lieu of an array, but
honestly I don't see why that's necessary. The obsolete link
referenced above implied for numerous delegates it's necessary, but I
think for 99% of programs an array as per the below or simple use of
+= chaining should be sufficient.

RL

///////////////////////////////////////////////////////////// OUTPUT,
as before, and as one would expect: /////////////////
hi:1
hi:10
Rays
begin delegate array
i, object are: 1234, @
i, object areTWO: 1234, @
end delegate array
notice chaining of delegates for x3
myFoo4()...nothing
The End
Press any key to continue . . .


//////////////////////////////////////////////////////////////////////////////////

class Program
{
static void Main(string[] args)
{
// http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

//Mediator m = new Mediator();
//Colleague2 head2 = new Colleague2(m, "lucy", 11, 'l');
//Colleague1 head1 = new Colleague1(m, "john", 10, 'j');

//head1.SendC1(10); //colleague1 is even numbers
//head2.SendC1(11);
//Console.WriteLine("Mediator typeof(T).
Module,.Name, .Namespace are- {0}, {1}, {2}", typeof(Mediator).Module,
typeof(Mediator).Name, typeof(Mediator).Namespace);
PropertyEventsSample myPES = new PropertyEventsSample();

// manual way
MyDelegate1 x = null;
// x += new MyDelegate1(myPES.myTest1.myFoo1); //old style
x += myPES.myTest1.myFoo1;
x(1); //works
x += new MyDelegate1(myPES.myTest1.myFoo12);
x(10); //works

//////// equivalent Jon Skeet start here

// p. 107 Albahari equivalent: ////////////////
MyDelegate2 x2 = new MyDelegate2(myPES.myTest1.myFoo2); //
I call this 'new style'
x2("Rays");

// MyDelegate2 x2 = null;
//x2 += new MyDelegate2(myPES.myTest1.myFoo2); //'old
style' using += operator (equivalent)
//x2("Ray");
///////////////////////////////////////////////

//MyDelegate3 x3 = null;
//x3 += new MyDelegate3(myPES.myTest1.myFoo3);
//x3(123, '!');
//x3 += new MyDelegate3(myPES.myTest1.myFoo32);
//x3(123, '$');

MyDelegate3 myDelegate3Instance1 = new
MyDelegate3(myPES.myTest1.myFoo3); //new style,compare with above
MyDelegate3 myDelegate3Instance2 = new
MyDelegate3(myPES.myTest1.myFoo32);

MyDelegate3 allInstances_of_Del3 = myDelegate3Instance1 +
myDelegate3Instance2; //pretty slick, eh Jon Skeet?

Delegate[] delegateList3 =
allInstances_of_Del3.GetInvocationList();
//key, to use .GetInvocationList, which returns an array of Delegate
<generic> ! This is key Jon, the use of the .GetInvocationList
extension method

Console.WriteLine("begin delegate array");

for (int counter = 0; counter < delegateList3.Length;
counter++)
{
((MyDelegate3)delegateList3[counter])(1234,'@'); //
note format for cast

}

Console.WriteLine("end delegate array");

// end here Jon Skeet


Console.WriteLine("notice chaining of delegates for x3");

MyDelegate4 x4 = null;
x4 += new MyDelegate4(myPES.myTest1.myFoo4);
x4();
}
 
R

raylopez99

Why are you trying to do this anyway? What's the overall goal? There's
almost certainly a better way of doing it.

Ok my long reply got lost in the ether. Suffice to say that you've
helped me a lot Jon. I concluded you should not mix delegates (1,2,3,
4 ) in the OP, regardless of container (whether hashtable or array)
and using .GetInvocation extension method helps in an array of
delegates. See MyDelegate3 below. Sorry I had a more complete reply
but it got lost.

RL


class Program
{
static void Main(string[] args)
{
// http://msdn.microsoft.com/en-us/library/8627sbea(VS.71).aspx

//Mediator m = new Mediator();
//Colleague2 head2 = new Colleague2(m, "lucy", 11, 'l');
//Colleague1 head1 = new Colleague1(m, "john", 10, 'j');

//head1.SendC1(10); //colleague1 is even numbers
//head2.SendC1(11);
//Console.WriteLine("Mediator typeof(T).
Module,.Name, .Namespace are- {0}, {1}, {2}", typeof(Mediator).Module,
typeof(Mediator).Name, typeof(Mediator).Namespace);
PropertyEventsSample myPES = new PropertyEventsSample();

// manual way
MyDelegate1 x = null;
// x += new MyDelegate1(myPES.myTest1.myFoo1); //old style
x += myPES.myTest1.myFoo1;
x(1); //works
x += new MyDelegate1(myPES.myTest1.myFoo12);
x(10); //works

//////// equivalent

// p. 107 Albahari equivalent: ////////////////
MyDelegate2 x2 = new MyDelegate2(myPES.myTest1.myFoo2);
x2("Rays");

// MyDelegate2 x2 = null;
//x2 += new MyDelegate2(myPES.myTest1.myFoo2);
//x2("Ray");
///////////////////////////////////////////////

//MyDelegate3 x3 = null;
//x3 += new MyDelegate3(myPES.myTest1.myFoo3);
//x3(123, '!');
//x3 += new MyDelegate3(myPES.myTest1.myFoo32);
//x3(123, '$');

MyDelegate3 myDelegate3Instance1 = new
MyDelegate3(myPES.myTest1.myFoo3);
MyDelegate3 myDelegate3Instance2 = new
MyDelegate3(myPES.myTest1.myFoo32);

MyDelegate3 allInstances_of_Del3 = myDelegate3Instance1 +
myDelegate3Instance2;

Delegate[] delegateList3 =
allInstances_of_Del3.GetInvocationList(); //key, to
use .GetInvocationList, which returns an array of Delegate <generic>

Console.WriteLine("begin delegate array");

for (int counter = 0; counter < delegateList3.Length;
counter++)
{
((MyDelegate3)delegateList3[counter])(1234,'@'); //
note format

}

Console.WriteLine("end delegate array");




Console.WriteLine("notice chaining of delegates for x3");

MyDelegate4 x4 = null;
x4 += new MyDelegate4(myPES.myTest1.myFoo4);
x4();
}


//OUTPUT (same as before, works)

hi:1
hi:10
Rays
begin delegate array
i, object are: 1234, @ //<-one of two MyDelegate3 methods
i, object areTWO: 1234, @ //<--two of 2
end delegate array
notice chaining of delegates for x3
myFoo4()...nothing
The End
Press any key to continue . . .
 
J

Jon Skeet [C# MVP]

Just to address this one bit:

Delegate[] delegateList3 =
allInstances_of_Del3.GetInvocationList();
//key, to use .GetInvocationList, which returns an array of Delegate
<generic> ! This is key Jon, the use of the .GetInvocationList
extension method

a) It's not an extension method
b) You don't need it in this case

If you just call a delegate which contains multiple actions, it will
call each of those actions with the same arguments.

You only need to call GetInvocationList and iterate through if you need
to do something with each result, or perhaps invoke all the actions
even if some of them throw an exception.

See P38-40 of C# in Depth for more on this.
 
R

raylopez99

If you just call a delegate which contains multiple actions, it will
call each of those actions with the same arguments.

Yes, I also noticed this.
You only need to call GetInvocationList and iterate through if you need
to do something with each result, or perhaps invoke all the actions
even if some of them throw an exception.

See P38-40 of C# in Depth for more on this.

pp. 38-40 of your book did not really bring this out, but yes, the C#
Cookbook recipe indeed use .GetInvocationList because in the array it
only invoked every other delegate (for expository purposes).

Thanks again

RL
 
B

Ben Voigt [C++ MVP]

The hashtable isn't storing hashtables, it's storing key/value pairs
I found a solution that does what I am trying to do--at least the
first of the two questions. If you have the book by O'Reilly "C#
Cookbook" by Hilyard and Teilhet, 2nd ed (Updated for C#2.0), look at
chapter 9, receipe 9.1 "Controlling when and if a delegate fires
within a multicast delegate". They use an array to store various
delegates, rather than a hash table as in my OP, then they traverse
the array using for() logic. They show how you can traverse backwards
and forwards and skip every other delegate, etc, as you can traversing
any array. Basically one of the two questions was this--how to store
disparate delegates in a container and traverse the container. Of
interest is that you can add delegates of disparate signatures,
exactly as I proposed, e.g., for those who don't have the book before
them reading this thread:

Ahh... you're mixing two different ideas for delegate containment. For
multiple delegates attached to a single event, just use the multicast
delegate, it's all managed for you, and use GetInvocationList if you need to
treat the delegates separately for some reason (such as Jon's suggestion of
isolating exception handling to each one individually).

The other one, the use of the Hashtable in the link you provided, has
nothing to do with multiple delegates on a single event. It's solely a
memory-saving measure. The built-in WinForms controls have hundreds of
events. In most cases no more than 3-4 of these actually have handlers
attached. If you used the "normal" way of using a multicast delegate field
for each event, you'd need a pointer-sized (4 or 8 bytes depending on x86 or
x64) chunk of memory in every control instance for every different possible
event. By using the Hashtable, you pay a higher cost per used event (for
the internal hashtable structure) but unused events cost nothing. To make
this work, there's really no way to avoid lots of casting, because you need
all the different kinds of events stuffed into a single Hashtable or
Dictionary (You could use Dictionary<object, EventHandler> instead of
Hashtable but you'd still need casts.)

I doubt you will ever need to use the Hashtable/Dictionary storage because
most classes you or I design have no more than a dozen or so events, so it's
more space efficient (and typesafe) to just use a field for each.
 
R

raylopez99

Ahh... you're mixing two different ideas for delegate containment.  For
multiple delegates attached to a single event, just use the multicast
delegate, it's all managed for you, and use GetInvocationList if you needto
treat the delegates separately for some reason (such as Jon's suggestion of
isolating exception handling to each one individually).

The other one, the use of the Hashtable in the link you provided, has
nothing to do with multiple delegates on a single event.  It's solely a
memory-saving measure.  The built-in WinForms controls have hundreds of
events.  In most cases no more than 3-4 of these actually have handlers
attached.  If you used the "normal" way of using a multicast delegate field
for each event, you'd need a pointer-sized (4 or 8 bytes depending on x86or
x64) chunk of memory in every control instance for every different possible
event.  By using the Hashtable, you pay a higher cost per used event (for
the internal hashtable structure) but unused events cost nothing.  To make
this work, there's really no way to avoid lots of casting, because you need
all the different kinds of events stuffed into a single Hashtable or
Dictionary (You could use Dictionary<object, EventHandler> instead of
Hashtable but you'd still need casts.)

I doubt you will ever need to use the Hashtable/Dictionary storage because
most classes you or I design have no more than a dozen or so events, so it's
more space efficient (and typesafe) to just use a field for each.

Excellent! Now it all makes sense. Your words will be hereby
memorialized in my library of wisdom on C# that I am steadily adding
to everyday...thank you.

BTW, I found there's several little traps for the unwary in delgates,
too numerous to mention, but here's one that I like:

// code fragment (don't get too bent out of shape as to what the
method are, just go with it. The point being: you can add Event 10
and Event 20 to form a combined delegate, but only if it's in the
proper format (what I call 'new style' but I think in fact it's pre
C#3 old style), namely using 'new' and WITHOUT the "+=" operator
(which itself is shorthand for a more complicated structure)... see
comments below.

Event10 = new MyDelegate1(myTest1.myFoo1); //'new style'

Event1 += new MyDelegate1(myTest1.myFoo1); //'old style'


Event20 = new MyDelegate1(myTest1.myFoo12);

Event2 += new MyDelegate1(myTest1.myFoo12);


// MyDelegate1 Events1_and_2 = Event1 + Event2;// does not
work! Error: "can only appear on the left hand side of += or -="
//error CS0079

MyDelegate1 Event10_and20 = Event10 + Event20; //but this,
DOES work, though it's exactly the same delegates!

// The end.

RL
 
J

Jon Skeet [C# MVP]

// code fragment (don't get too bent out of shape as to what the
method are, just go with it. The point being: you can add Event 10
and Event 20 to form a combined delegate, but only if it's in the
proper format (what I call 'new style' but I think in fact it's pre
C#3 old style), namely using 'new' and WITHOUT the "+=" operator
(which itself is shorthand for a more complicated structure)... see
comments below.

Event10 = new MyDelegate1(myTest1.myFoo1); //'new style'
Event1 += new MyDelegate1(myTest1.myFoo1); //'old style'

Both of those would have been valid in C# 1, assuming Event10 really is
referencing a variable rather than an event. Neither is a "new style".
The difference is that one is using assignment, and the other is
combining the two delegates - the result seems pretty obvious to me!

What *is* "new" in C# 2 is the ability to write:

Event10 = myTest1.myFoo1;
Event1 += myText1.myFoo1;
Event20 = new MyDelegate1(myTest1.myFoo12);

Event2 += new MyDelegate1(myTest1.myFoo12);


// MyDelegate1 Events1_and_2 = Event1 + Event2;// does not
work! Error: "can only appear on the left hand side of += or -="
//error CS0079

Yes, you can only subscribe/unsubscribe from an event, not assign to it
or fetch a "current" value.
MyDelegate1 Event10_and20 = Event10 + Event20; //but this,
DOES work, though it's exactly the same delegates!

My guess is that Event1 and Event2 are proper events, whereas Event10
and Event20 are variables (or field-like events in the same class).
 
Top