107

How do I create a Dictionary where I can store functions?

Thanks.

I have about 30+ functions which can be executed from the user. I want to be able to execute the function this way:

   private void functionName(arg1, arg2, arg3)
   {
       // code
   }

   dictionaryName.add("doSomething", functionName);

    private void interceptCommand(string command)
    {
        foreach ( var cmd in dictionaryName )
        {
            if ( cmd.Key.Equals(command) )
            {
                cmd.Value.Invoke();
            }
        }
    }

However, the function signature is not always the same, thus having different amount of arguments.

chi
  • 1,073
  • 2
  • 8
  • 5
  • 10
    This is a great idiom - can replace nasty switch statements. – Hamish Grubijan Nov 20 '10 at 15:47
  • Your example function has parameters, however, when you invoke the stored function you are invoking it without any arguments. Are the arguments fixed when the function is stored? – Tim Lloyd Nov 20 '10 at 16:12
  • 1
    That was just a poorly written example by myself. I don't know to create a Dictionary that would do it, because the function signature is always different. – chi Nov 20 '10 at 16:17
  • @chi I am assuming then, that functions have different signatures and you wish to invoke the stored function with the appropriate arguments. – Tim Lloyd Nov 20 '10 at 16:19
  • @chi Do the methods have return values? – Tim Lloyd Nov 20 '10 at 16:29
  • What advantage do you get storing functions in a table rather than using vanilla OOP with virtual functions? – Juliet Nov 20 '10 at 17:04
  • @juliet Well, I'm interested in this question because I'm looking at a behemoth of a Command pattern implementation I'd like to reduce by an order of magnitude using a more functional-oriented approach. – Erik Reppen Feb 11 '13 at 22:29
  • 1
    @HamishGrubijan, if you do this to replace a switch statement then you are discarding all the compile time optimization and clarity. So, I'd say it was more idiot than idiom. If you want to dynamically map functionality in a way that could vary at runtime, then, it might be useful. – Jodrell Jul 01 '13 at 10:32
  • This is essentially a wrong idea, use a `switch` statment. If you want to dynamically assign functionality at runtime, then a dictionary will not allow you to store typed delegates of different types. – Jodrell Jul 01 '13 at 10:35
  • 19
    @Jodrell, A) Idiot is a strong word that is not constructive, B) Clarity is in the eyes of a beholder. I have seen plenty of ugly switch statements. C) Compile time optimization ... Zooba on this threads argues for the opposite http://stackoverflow.com/questions/505454/large-switch-statements-bad-oop If switch statements is O(log N), then it would have to contain 100+ cases for speed to make a difference. That is not readable, of course. Maybe a switch statement can utilize a perfect hash function, but only for a small # of cases. If you are using .Net, then you do not fret over microseconds. – Hamish Grubijan Jul 02 '13 at 00:21
  • 6
    @Jodrell, (continued) If you want to squeeze the most out of your hardware, then you use ASM, C, or C++ in that order. Dictionary lookup in .Net will not kill you. Various signatures of the delegates - that is the strongest point that you made against using a simple dictionary approach. – Hamish Grubijan Jul 02 '13 at 00:23
  • For anyone googling here, you usually just use **Action** and that's it. – Fattie Nov 19 '20 at 15:36
  • FTR Some of the comments here are strange. This is the absolutely, totally, completely, SOP, normal way to map "commands to commands", so, commands are received from your server or the like and then you execute different blocks of code in response. (the "performance" comments are whacky) – Fattie Nov 19 '20 at 15:44

6 Answers6

143

Like this:

Dictionary<int, Func<string, bool>>

This allows you to store functions that take a string parameter and return boolean.

dico[5] = foo => foo == "Bar";

Or if the function is not anonymous:

dico[5] = Foo;

where Foo is defined like this:

public bool Foo(string bar)
{
    ...
}

UPDATE:

After seeing your update it seems that you don't know in advance the signature of the function you would like to invoke. In .NET in order to invoke a function you need to pass all the arguments and if you don't know what the arguments are going to be the only way to achieve this is through reflection.

And here's another alternative:

class Program
{
    static void Main()
    {
        // store
        var dico = new Dictionary<int, Delegate>();
        dico[1] = new Func<int, int, int>(Func1);
        dico[2] = new Func<int, int, int, int>(Func2);

        // and later invoke
        var res = dico[1].DynamicInvoke(1, 2);
        Console.WriteLine(res);
        var res2 = dico[2].DynamicInvoke(1, 2, 3);
        Console.WriteLine(res2);
    }

    public static int Func1(int arg1, int arg2)
    {
        return arg1 + arg2;
    }

    public static int Func2(int arg1, int arg2, int arg3)
    {
        return arg1 + arg2 + arg3;
    }
}

With this approach you still need to know the number and type of parameters that need to be passed to each function at the corresponding index of the dictionary or you will get runtime error. And if your functions doesn't have return values use System.Action<> instead of System.Func<>.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I see. I guess I will have to spend some time reading about reflection then, appreciate the help. – chi Nov 20 '10 at 16:24
  • @chi, see my last update. I've added an example with `Dictionary`. – Darin Dimitrov Nov 20 '10 at 16:33
  • Also, with the `Delegate()` model, can't you compound the `dico` values: `dico[1] += new Func<...>(Func3)` so now dico[1] executes Func1 then Func3 ? – IAbstract Nov 20 '10 at 16:37
  • 3
    I won't downvote, but I will just have to say this sort of implementation is an antipattern without **very** good reasons. If the OP expect clients to pass in all the arguments to the function, then why have a function table in the first place? Why not have the client simply *call* the functions without the magic of dynamic invokes? – Juliet Nov 20 '10 at 17:06
  • 1
    @Juliet, I agree with you, I never said that's a good thing. By the way in my answer I stressed on the fact that we still need to know the number and type of parameters that need to be passed to each function at the corresponding index of the dictionary. You are absolutely correct by pointing out that in this case we probably don't even need a hashtable as we could directly invoke the function. – Darin Dimitrov Nov 20 '10 at 19:15
  • 2
    What if I need to store a function that does not take any arguments and has no return value? – DataGreed Sep 17 '19 at 14:49
11

However, the function signature is not always the same, thus having different amount of arguments.

Let's start with a few functions defined like this:

private object Function1() { return null; }
private object Function2(object arg1) { return null; }
private object Function3(object arg1, object arg3) { return null; }

You really have 2 viable options at your disposal:

1) Maintain type-safety by having clients call your function directly.

This is probably the best solution, unless you have very good reasons for breaking from this model.

When you talk about wanting to intercept function calls, it sounds to me like you're trying to re-invent virtual functions. There's a boat load of ways to get this sort of functionality out of the box, such as inheriting from a base class an overriding its functions.

It sounds to me like you want a class that's more of a wrapper than a derived instance of a base class, so do something like this:

public interface IMyObject
{
    object Function1();
    object Function2(object arg1);
    object Function3(object arg1, object arg2);
}

class MyObject : IMyObject
{
    public object Function1() { return null; }
    public object Function2(object arg1) { return null; }
    public object Function3(object arg1, object arg2) { return null; }
}

class MyObjectInterceptor : IMyObject
{
    readonly IMyObject MyObject;

    public MyObjectInterceptor()
        : this(new MyObject())
    {
    }

    public MyObjectInterceptor(IMyObject myObject)
    {
        MyObject = myObject;
    }

    public object Function1()
    {
        Console.WriteLine("Intercepted Function1");
        return MyObject.Function1();
    }
    public object Function2(object arg1)
    {
        Console.WriteLine("Intercepted Function2");
        return MyObject.Function2(arg1);
    }

    public object Function3(object arg1, object arg2)
    {
        Console.WriteLine("Intercepted Function3");
        return MyObject.Function3(arg1, arg2);
    }
}

2) OR map the input of your functions to a common interface.

This might work if all of your functions are related. For example, if you're writing a game, and all the functions do something to some part of the player or player's inventory. You'd end up with something like this:

class Interceptor
{
    private object function1() { return null; }
    private object function2(object arg1) { return null; }
    private object function3(object arg1, object arg3) { return null; }

    Dictionary<string, Func<State, object>> functions;

    public Interceptor()
    {
        functions = new Dictionary<string, Func<State, object>>();
        functions.Add("function1", state => function1());
        functions.Add("function2", state => function2(state.arg1, state.arg2));
        functions.Add("function3", state => function3(state.arg1, state.are2, state.arg3));
    }

    public object Invoke(string key, object state)
    {
        Func<object, object> func = functions[key];
        return func(state);
    }
}
Juliet
  • 80,494
  • 45
  • 196
  • 228
7

Define the dictionary and add the function reference as the value, using System.Action as the type:

using System.Collections;
using System.Collections.Generic;

public class Actions {

    public Dictionary<string, System.Action> myActions = new Dictionary<string, System.Action>();

    public Actions() {
        myActions ["myKey"] = TheFunction;
    }

    public void TheFunction() {
        // your logic here
    }
}

Then invoke it with:

Actions.myActions["myKey"]();
modle13
  • 1,242
  • 2
  • 16
  • 16
3

The following scenario would allow you to use a dictionary of elements to send in as input parameters and get the same as the output parameters.

First add the following line at the top:

using TFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.IDictionary<string, object>>;

Then inside your class, define the dictionary as follows:

     private Dictionary<String, TFunc> actions = new Dictionary<String, TFunc>(){

                        {"getmultipledata", (input) => 
                            {
                                //DO WORKING HERE
                                return null;
                            } 
                         }, 
                         {"runproc", (input) => 
                            {
                                //DO WORKING HERE
                                return null;
                            } 
                         }
 };

This would allow you to run these anonymous functions with a syntax similar to this:

var output = actions["runproc"](inputparam);
Farax
  • 1,447
  • 3
  • 20
  • 37
3

Hey, I hope this helps. What language are you coming from?

internal class ForExample
{
    void DoItLikeThis()
    {
        var provider = new StringMethodProvider();
        provider.Register("doSomethingAndGetGuid", args => DoSomeActionWithStringToGetGuid((string)args[0]));
        provider.Register("thenUseItForSomething", args => DoSomeActionWithAGuid((Guid)args[0],(bool)args[1]));


        Guid guid = provider.Intercept<Guid>("doSomethingAndGetGuid", "I don't matter except if I am null");
        bool isEmpty = guid == default(Guid);
        provider.Intercept("thenUseItForSomething", guid, isEmpty);
    }

    private void DoSomeActionWithAGuid(Guid id, bool isEmpty)
    {
        // code
    }

    private Guid DoSomeActionWithStringToGetGuid(string arg1)
    {
        if(arg1 == null)
        {
            return default(Guid);
        }
        return Guid.NewGuid();
    }

}
public class StringMethodProvider
{
    private readonly Dictionary<string, Func<object[], object>> _dictionary = new Dictionary<string, Func<object[], object>>();
    public void Register<T>(string command, Func<object[],T> function)
    {
        _dictionary.Add(command, args => function(args));
    }
    public void Register(string command, Action<object[]> function)
    {
        _dictionary.Add(command, args =>
                                     {
                                         function.Invoke(args);
                                         return null;
                                     } );
    }
    public T Intercept<T>(string command, params object[] args)
    {
        return (T)_dictionary[command].Invoke(args);
    }
    public void Intercept(string command, params object[] args)
    {
        _dictionary[command].Invoke(args);
    }
}
smartcaveman
  • 41,281
  • 29
  • 127
  • 212
1

Why not use params object[] list for method parameters and do some validation inside either your methods (or calling logic), It will allow for a variable number of parameters.

wvd_vegt
  • 326
  • 2
  • 5