tags:

views:

136

answers:

4

I wanted to have a hashtable with a string as key and a functionpointer (delegate) as value. This way calling the correct routine given a string based command. However, the compiler won't eat it.

What am I doing wrong?

//declaration
     public delegate void categoryHandler(String request);     

//init code
     Hashtable categories = new Hashtable();
     categories.Add("campaigns", Campaigns.post);

//function call
     String category = "campaigns";
     categoryHandler handler = (categoryHandler) categories[category];
     if (handler != null)
     {
          handler(someString);
     }

//handler
     static public void post(String request)
     {
          ...
     }

The error I get is on the line where I put the function in the hashtable: Error 2 Argument '2': cannot convert from 'method group' to 'object'

I'm hoping it is just some semantic thingy I forgot... But if this can't be done... is there another way to have some kind of String based jumptable?

A: 

Depending on the version of C# that you're using, you may need to do:

categories.Add("campaigns", new categoryHandler(Campaigns.post));

As an aside, if you're using .NET 2.0 or above, you should be using the generic Dictionary<T,T> class instead of Hashtable.

Adam Robinson
+6  A: 

The problem is that you're using Hashtable which is weakly typed. The compiler sees the method group (the name of the method you want to convert into a delegate) but doesn't know what delegate type you mean.

If you want to keep using Hashtable, you could do:

categoryHandler handler = Campaigns.post;
categories.Add("campaigns", handler);

or

categories.Add("campaigns", new categoryHandler(Campaigns.post));

In both cases, the method group is being convert to the specific delegate type, so it's okay.

However, a better solution is to use Dictionary<string, categoryHandler> in the first place - always use strongly typed collections where you can sensibly do so (which is almost always). For the sake of convention, it should be CategoryHandler btw - it's the name of a type. Likewise post should be Post.

Then to call it, you'd use:

 String category = "campaigns";
 CategoryHandler handler;
 if (categories.TryGetValue(category, out handler))
 {
     handler(someString);
 }
Jon Skeet
Although I wonder whether it just won't work with a `HashTable`. After all, `Delegate` *does* inherit from `Object`.
Joey
...I never realized there was a 'TryGetValue' on Dictionaries... All this time, I've been doing if (dict.ContainsKey(foo)){dict[foo]}... dang.
GWLlosa
@Johannes: Editing to explain why it's failing.
Jon Skeet
great thanks!
Toad
Ah thanks. Not that I'd use a Hashtable for this but a simple "use this instead" often enough doesn't really help much :-)
Joey
+1  A: 

Don't use a hashtable use the Dictionary

Your code will change too.

//declaration
     public delegate void categoryHandler(String request);     

//init code
     Dictionary<string, categoryHandler> categories = new Dictionary<string, categoryHandler> ;
     categories.Add("campaigns", Campaigns.post);

//function call
     string category = "campaigns";

     if (!categories.ContainsKey(category))
     {
        // Key not there just return
        return;
     }

     categoryHandler handler = categories[category];  // NO need to cast here

     if (handler != null)
     {
          handler(someString);
     }

//handler
     static public void post(String request)
     {
          ...
     }
David Basarab
Although there's no need to cast, the dictionary indexer will throw an exception when the key isn't present, instead of returning null.
Jon Skeet
That is true. I will modify.
David Basarab
great thanks!
Toad
+2  A: 

If you are using .Net 3.5, you can do what I do when I want to eliminate switch statements:

private readonly Dictionary<string, Action<string>> _lookupTable = new Dictionary<string, Action<string>>
{
    {"campaigns", post}
    {"somethingElse", doSomethingElse}
    {"tryIt", val => doSomethingWithVal(val)} 
};

then, where I would have a switch statement, I would do this:

_lookupTable["foo"]("bar");
Brian Genisio
very compact and readable this solution!
Toad