1

I've seen posts here on how to make a dictionary that has multiple values per key, like one of the solutions presented in this link:

Multi Value Dictionary

it seems that I have to use a List<> as the value for the keys, so that a key can store multiple values.

the solution in the link is fine if you want to add values. But my problem now is how to remove specific values from a single key.

I have this code for adding values to a dictionary:

private Dictionary<TKey, List<TValue>> mEventDict;
    // this is for initializing the dictionary


public void Subscribe(eVtEvtId inEvent, VtEvtDelegate inCallbackMethod)
    {
        if (mEventDict.ContainsKey(inEvent))
        {
            mEventDict[inEvent].Add(inCallbackMethod);
        }
        else
        {
            mEventDict.Add(inEvent, new List<TValue>() { v });
        }
    }
// this is for adding values to the dictionary.
// if the "key" (inEvent) is not yet present in the dictionary,
// the key will be added first before the value

my problem now is removing a specific value from a key. I have this code:

public void Unsubscribe(eVtEvtId inEvent, VtEvtDelegate inCallbackMethod)
    {
        try
        {
            mEventDict[inEvent].Remove(inCallbackMethod);
        }

        catch (ArgumentNullException)
        {
            MessageBox.Show("The event is not yet present in the dictionary");
        }
    }

basically, what I did is just replace the Add() with Remove() . Will this work?

Also, if you have any problems or questions with the code (initialization, etc.), feel free to ask.

Thanks for the advice.

Community
  • 1
  • 1
Anthony
  • 437
  • 2
  • 8
  • 18
  • 2
    "will this work?" You've already written the code; why don't you run it and find out? – phoog Oct 17 '12 at 03:39
  • It kind of depends on what `inCallbackMethod` is... is it the same type as `TValue`? – Dave Zych Oct 17 '12 at 03:42
  • @phoog - what I mean by "will this work" is, is that a proper usage of the Remove() ? or should the structure of the Remove() code be different as compared to the Add() code? – Anthony Oct 17 '12 at 03:58
  • #Dave Zych - Yes, {inCallbackMethod} is the same type as {TValue} . – Anthony Oct 17 '12 at 04:00

3 Answers3

2

TylerOhlsen's answer is a step in the right direction, but it has 6 key lookups (calls to Remove, ContainsKey, and the indexer). This can be reduced to three by using TryGetValue:

private Dictionary<TKey, List<TValue>> mEventDict;

public void Subscribe(TKey inEvent, TValue inCallbackMethod)
{
    List<TValue> list;

    if (mEventDict.TryGetValue(inEvent, out list))
        list.Add(inCallbackMethod);
    else
        mEventDict.Add(inEvent, new List<TValue> { inCallbackMethod });
}

public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
    List<TValue> list;

    if (!mEventDict.TryGetValue(inEvent, out list))
        return false;

    bool removed = list.Remove(inCallbackMethod);

    if (list.Count == 0)
        mEventDict.Remove(inEvent);

    return removed;
}

If you don't care about removing empty lists:

public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
    List<TValue> list;

    if (!mEventDict.TryGetValue(inEvent, out list))
        return false;

    return list.Remove(inCallbackMethod);
}

If you don't need to report whether the item was in the list (and therefore removed from it), change the return type to void, and (in the first version) get rid of the removed variable.

phoog
  • 42,068
  • 6
  • 79
  • 117
  • I wasn't actually aware of TryGetValue. Thanks for that. – TylerOhlsen Oct 17 '12 at 05:23
  • how about if I do not want to change the names of the parameters in the Subscribe and Unsubscribe methods? Do I change the names of the parameters in the Dictionary according to the functions instead? Also, if I choose to keep empty lists in the Dictionary (for whatever reason) , do I just change the type of the Unsubscribe from bool to void and take out the code that's involved with removing of a list? because at the moment, I'm only concerned with removing values from a list. – Anthony Oct 17 '12 at 06:26
  • @Anthony Do you mean the type parameters or the formal parameters (the method parameters)? See edited code for not removing lists; and yes, you can change it to a void method (whether or not you're removing the lists from the dictionary). – phoog Oct 17 '12 at 06:33
  • i meant the method parameters. if I do not want to change them, should I change the name of the parameters in the Dictionary instead, so as to be in accordance to the methods? – Anthony Oct 17 '12 at 08:51
  • what I meant is setting the Dictionary parameters to this: `private Dictionary> mEventDict;` would this be fine? – Anthony Oct 17 '12 at 23:58
  • @Anthony Yes, I suspect you must use `Dictionary>`; if your class doesn't have type parameters named `TKey` and `TValue` then your code won't compile. – phoog Oct 18 '12 at 22:28
  • Actually, your code would only compile if the class had those type parameters *and* if they were constrained `where TKey : eVtEvtId where TValue : VtEvtDelegate` – phoog Oct 18 '12 at 22:42
1

Will it work? Not exactly the way you intended...

  • Your method parameters will need to be of the generic types.
  • List(T).Remove does not throw an ArgumentNullException.
  • You might want to clean up your dictionary if your list becomes empty.
  • The caller might not care if the callback was ever subscribed when they unsubscribe, but you have that information so you might as well return it. This information could be helpful for troubleshooting/logging purposes.

This is what I would recommend...

private Dictionary<TKey, List<TValue>> mEventDict;

public void Subscribe(TKey inEvent, TValue inCallbackMethod)
{
    if (!mEventDict.ContainsKey(inEvent))
        mEventDict.Add(inEvent, new List<TValue>());

    mEventDict[inEvent].Add(inCallbackMethod);
}

public bool Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
    if (!mEventDict.ContainsKey(inEvent))
        return false;

    bool removed = mEventDict[inEvent].Remove(inCallbackMethod);

    if (mEventDict[inEvent].Count == 0)
        mEventDict.Remove(inEvent);

    return removed;
}

NOTE: I have not tested this code, so just try it out. Also, this code is not thread safe.

TylerOhlsen
  • 5,485
  • 1
  • 24
  • 39
0

@phoog - so I want to keep the Unsubscribe method as void . After modifying your code, this is what I came up with...

public void Unsubscribe(TKey inEvent, TValue inCallbackMethod)
{
    List<TValue> list;

    bool mRemoved = false.

    if (mEventDict.TryGetValue(inEvent, out list))
    {
        list.Remove(inCallbackMethod);
        mRemoved = true;
    }
}

is the listRemoved variable necessary? But then again, I think nothing will happen if the inCallbackMethod cannot be found in the list.

Anthony
  • 437
  • 2
  • 8
  • 18
  • the reason I switched back the `Unsubscribe` method to `void` is because I don't want it to return any value. I just want it to remove a specific value from a list. – Anthony Oct 18 '12 at 00:26