Static dictionary containing delegates to instance

2020-07-09 10:19发布

问题:

I have this method with a huge switch statement like this:

public bool ExecuteCommand(string command, string args)
{
    bool result = false;
    switch (command)
    {
        case "command1": result = Method1(args); break;
        case "command2": result = Method2(args); break;
        // etc.
    }
    return result;
}
private bool Method1(string args) {...}

Now I thought about replacing this with a dictionary of Func<> delegates so that I can eliminate the switch statement:

private Dictionary<string, Func<string, bool>> _commands = new ...;
public MyClass()
{
    _commands.Add("command1", Method1);
    // etc:
}
public bool ExecuteCommand(string command, string args)
{
    return _commands[command](args);
}

The problem I see with this, is that a new Dictionary is instantiated and populated with each new instance of MyClass.

Is it possible to somehow make that Dictionary (containing delegates to instance methods) a static member, which would be initialized only once, in the static constructor?

E.g. something like this (does not work):

private static Dictionary<string, Func<string, bool>> _commands = new ...;
static MyClass()
{
    // the following line will result in a compiler error:
    // error CS0120: An object reference is required for the non-static field,
    // method, or property 'MyClass.Method1(string, string)'
    _commands.Add("command1", MyClass.Method1);
}

回答1:

You can initialize it in the static constructor - but you'll need to create instances of MyClass, which may not be what you want, because I assume you want the command to execute "in the context of" the instance which Execute has been called on.

Alternatively, you can populate the dictionary with delegates which take an instance of MyClass as well, like this:

class MyClass
{
    static Dictionary<string, Func<MyClass, string, bool>> commands
        = new Dictionary<string, Func<MyClass, string, bool>>
    {
        { "Foo", (@this, x) => @this.Foo(x) },
        { "Bar", (@this, y) => @this.Bar(y) }
    };

    public bool Execute(string command, string value)
    {
        return commands[command](this, value);
    }

    public bool Foo(string x)
    {
        return x.Length > 3;
    }

    public bool Bar(string x)
    {
        return x == "";
    }
}

In theory I believe it should be doable without the lambda expression by creating an "open delegate", but it would need a bit more work using reflection. If you don't mind the ugliness and tiny performance penalty of the extra indirection, I think this approach should work quite well.



标签: c# delegates