Capturing Dictionary<T>.Add() event?

B

Brett Romero

If I want to take action on the Add event of a generic Dictionary, do I
need to create a custom Dictionary and add an event handler for the
Add() method? The dictionary is a public field on a custom control.

Thanks,
Brett
 
B

Barry Kelly

Brett Romero said:
If I want to take action on the Add event of a generic Dictionary, do I
need to create a custom Dictionary and add an event handler for the
Add() method? The dictionary is a public field on a custom control.

The existing Dictionary<,> generic class uses non-virtual methods for
speed reasons, for those people who need the speed.

If you want to intercept these kinds of events, consider descending from
the KeyedCollection<,> class, or write your own class that implements
IDictionary<,> and delegates actual storage to a Dictionary<,>.

-- Barry
 
B

Brett Romero

I went ahead with adding an Add() event handler. If any one is
interested:

public class CustomDictionary<TKey, TValue> : Dictionary<TKey,
TValue>
{
public event AddEventHandler AddEvent;

public CustomDictionary()
{

}

public void Add(TKey pKey, TValue pValue)
{
if( AddEvent != null )
AddEvent( new AddEventArgs(pKey, pValue) );
base.Add( pKey, pValue );
}

public delegate void AddEventHandler( AddEventArgs
pAddEventArgs );

public class AddEventArgs : EventArgs
{
private TKey _key;
private TValue _value;

public AddEventArgs( TKey key, TValue value )
{

}

public TKey Key
{
get
{
return _key;
}
}

public TValue Value
{
get
{
return _value;
}
}
}
}


Brett
 
B

Barry Kelly

Brett Romero said:
I went ahead with adding an Add() event handler. If any one is
interested:

public class CustomDictionary<TKey, TValue> : Dictionary<TKey,
TValue>

Be careful: any client code can evade your event notification by simply
casting the dictionary to Dictionary<TKey,TValue> and calling Add()
directly. So, if you expect this event to be invoked every time the
Add() method is called on your public property, you will be
disappointed!

-- Barry
 
B

Brett Romero

If you want to intercept these kinds of events, consider descending from
the KeyedCollection<,> class, or write your own class that implements
IDictionary<,> and delegates actual storage to a Dictionary<,>.

I don't notice any difference in performance. Just not enough data or
use of this to make any difference. I don't need everything that
IDictionary<,> will require. Rewiring to work with KeyCollection<,>
isn't worth the effort and I'm unsure if it will in the end accomplish
what I already have. Inheriting from Dictionary<,> is doing
everything I need with good performance.

No one will be casting this either.

Thanks,
Brett
 
B

Barry Kelly

Brett Romero said:
I don't notice any difference in performance.

It's not about performance: it's about code reliability. If you're
depending on your event being called, people can evade it by casting to
the base class.
No one will be casting this either.

If it's on a public interface, you can't guarantee anything about how
it's going to be used, unless it follows the rules of the CLR framework.

It's easy to cast by accident if you've got worker methods that work
with IDictionary<,>, for example. I do things like this all the time.
YMMV!

-- Barry
 
B

Brett Romero

I guess I'd need to see an example and explanation of why you'd want to
cast a generic. Isn't that defeating the purpose? It won't be casted
in my cast and I can guarantee that. The added complexity of a cast
will never be used.

Brett
 
B

Barry Kelly

Brett Romero said:
I guess I'd need to see an example and explanation of why you'd want to
cast a generic. Isn't that defeating the purpose? It won't be casted
in my cast and I can guarantee that. The added complexity of a cast
will never be used.

This is contrived to make a point:

---8<---
static public class Util
{
public static void AddEntry<TKey,TValue>(
Dictionary<TKey,TValue> dict, TKey key, TValue value)
{
dict.Add(key, value);
}
}
--->8---

Pass the dictionary that is part of the public interface of your class
to this method (Util.AddEntry), and it will avoid your hook on the Add
method.

The cast is implicit, and you can do nothing to guarantee it won't be
used if the class with this custom dictionary is a public part of a
library.

-- Barry
 
B

Brett Romero

Pass the dictionary that is part of the public interface of your class
to this method (Util.AddEntry), and it will avoid your hook on the Add
method.

The cast is implicit, and you can do nothing to guarantee it won't be
used if the class with this custom dictionary is a public part of a
library.

Not if you add the event there also:

static public class Util
{
public event Mydict.AddEventHandler AddEvent;

public static void AddEntry<TKey,TValue>(
Dictionary<TKey,TValue> dict, TKey key, TValue value)

{
if( AddEvent != null )
Mydict.AddEvent( new AddEventArgs(pKey, pValue) );
dict.Add(key, value);
}
}

I just can't think of a practical reason why you'd want to do such a
thing in the first place. Not to say one doesn't exists.

Brett
 
B

Barry Kelly

Brett Romero said:
Not if you add the event there also:

You're missing the point - I, the client of *your* code, possibly on the
other side of the world and speaking in a different language, wrote the
AddEntry method *myself*. I have no responsibility to call *your* event.
That is what I mean by pointing out that you've got no guarantee that
*your* event will be called.

-- Barry
 
B

Brett Romero

Yes - you are correct when I have no control over the environment that
this code will be used in. However, I do have complete control so it
is a safe bet.

Thanks,
Brett
 
G

Giulio Petrucci

Hi Barry,
Hi everybody,

Barry Kelly ha scritto:
Be careful: any client code can evade your event notification by simply
casting the dictionary to Dictionary<TKey,TValue> and calling Add()
directly. So, if you expect this event to be invoked every time the
Add() method is called on your public property, you will be
disappointed!

I agree with your suggestion.
I think that the better way is to wrap a Dictionary<TKey,TValue> with a
wrapper class implementing the IDictionary<,> interface, declaring

private Dictionary<,> innerDictionary

within this new class. So that no one can access the member fo the inner
dictionary (without event handling). Am I right? I ask you because I'm
quite "new" in C# programming...

Thanks,
Giulio
 
B

Barry Kelly

Giulio Petrucci said:
Hi Barry,
Hi everybody,

Barry Kelly ha scritto:


I agree with your suggestion.
I think that the better way is to wrap a Dictionary<TKey,TValue> with a
wrapper class implementing the IDictionary<,> interface, declaring

private Dictionary<,> innerDictionary

within this new class. So that no one can access the member fo the inner
dictionary (without event handling). Am I right? I ask you because I'm
quite "new" in C# programming...

Sure. And the KeyedCollection<T> class already does most of what most
people require, so that they simply need to override the InsertItem/ etc
methods.

-- Barry
 

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