【问题标题】:C# Store functions in a DictionaryC# 将函数存储在字典中
【发布时间】:2011-05-13 02:57:21
【问题描述】:

如何创建可以存储函数的字典?

谢谢。

我有大约 30 多个可以由用户执行的功能。我希望能够以这种方式执行该功能:

   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();
            }
        }
    }

但是,函数签名并不总是相同的,因此具有不同数量的参数。

【问题讨论】:

  • 这是一个很棒的成语——可以代替讨厌的 switch 语句。
  • 那只是我自己写的一个糟糕的例子。我不知道创建一个可以做到这一点的字典,因为函数签名总是不同的。
  • @HamishGrubijan,如果您这样做是为了替换 switch 语句,那么您将放弃所有编译时优化和清晰度。所以,我会说它比成语更白痴。如果您想以一种在运行时可能会发生变化的方式动态映射功能,那么它可能会很有用。
  • @Jodrell,A) 白痴是一个没有建设性的强词,B) 旁观者眼中的清晰。我见过很多丑陋的 switch 语句。 C) 编译时间优化...此线程上的 Zooba 反对 stackoverflow.com/questions/505454/… 如果 switch 语句为 O(log N),那么它必须包含 100 多个案例才能使速度有所作为。当然,这是不可读的。也许 switch 语句可以利用完美的散列函数,但仅适用于少数情况。如果您使用的是 .Net,那么您不必担心微秒。
  • @Jodrell,(续)如果您想充分利用硬件,那么您可以按顺序使用 ASM、C 或 C++。 .Net 中的字典查找不会杀死你。代表的各种签名 - 这是您反对使用简单字典方法的最强点。

标签: c# function pointers dictionary


【解决方案1】:

像这样:

Dictionary<int, Func<string, bool>>

这允许您存储接受字符串参数并返回布尔值的函数。

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

或者如果函数不是匿名的:

dico[5] = Foo;

Foo 的定义如下:

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

更新:

看到您的更新后,您似乎事先并不知道要调用的函数的签名。在 .NET 中,为了调用一个函数,您需要传递所有参数,如果您不知道参数是什么,那么实现此目的的唯一方法就是通过反射。

还有另一种选择:

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;
    }
}

使用这种方法,您仍然需要知道需要在字典的相应索引处传递给每个函数的参数的数量和类型,否则会出现运行时错误。如果您的函数没有返回值,请使用System.Action&lt;&gt; 而不是System.Func&lt;&gt;

【讨论】:

  • 我明白了。我想我将不得不花一些时间阅读反射,感谢帮助。
  • @chi,查看我的最新更新。我添加了一个带有 Dictionary&lt;int, Delegate&gt; 的示例。
  • 我不会投反对票,但我只想说这种实现是一种反模式,没有非常充分的理由。如果 OP 希望客户端将所有参数传递给函数,那么为什么首先要有一个函数表?为什么不让客户端简单地调用没有动态调用魔力的函数?
  • @Juliet,我同意你的观点,我从来没有说过这是一件好事。顺便说一句,在我的回答中,我强调了一个事实,即我们仍然需要知道需要在字典的相应索引处传递给每个函数的参数的数量和类型。您指出在这种情况下我们可能甚至不需要哈希表,因为我们可以直接调用该函数,所以您是绝对正确的。
  • 如果我需要存储一个不带任何参数且没有返回值的函数怎么办?
【解决方案2】:

但是,函数签名不是 总是一样的,因此有不同的 论据的数量。

让我们从几个这样定义的函数开始:

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

您确实有两个可行的选择:

1) 通过让客户直接调用您的函数来保持类型安全。

这可能是最好的解决方案,除非您有非常充分的理由打破这种模式。

当您谈到想要拦截函数调用时,在我看来您是在尝试重新发明虚函数。有很多方法可以开箱即用地获得这种功能,例如从基类继承并覆盖其功能。

在我看来,您想要一个更像包装器的类,而不是基类的派生实例,所以请执行以下操作:

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) 或者将函数的输入映射到一个通用接口。

如果您的所有功能都相关,这可能会起作用。例如,如果您正在编写游戏,并且所有函数都对玩家或玩家库存的某些部分执行某些操作。你最终会得到这样的结果:

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);
    }
}

【讨论】:

  • 我的假设是object,就像将传递给新线程一样。
【解决方案3】:

定义字典并添加函数引用作为值,使用System.Action作为类型:

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
    }
}

然后调用它:

Actions.myActions["myKey"]();

【讨论】:

  • 到目前为止,99% 的案例中的常用答案。
  • 正是我需要的。不胜感激!! ??
【解决方案4】:

嘿,我希望这会有所帮助。你来自什么语言?

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);
    }
}

【讨论】:

    【解决方案5】:

    为什么不将params object[] list 用于方法参数并在您的方法(或调用逻辑)内部进行一些验证,它将允许可变数量的参数。

    【讨论】:

      【解决方案6】:

      以下场景将允许您使用元素字典作为输入参数发送并获得与输出参数相同的内容。

      首先在顶部添加以下行:

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

      然后在你的类中,定义字典如下:

           private Dictionary<String, TFunc> actions = new Dictionary<String, TFunc>(){
      
                              {"getmultipledata", (input) => 
                                  {
                                      //DO WORKING HERE
                                      return null;
                                  } 
                               }, 
                               {"runproc", (input) => 
                                  {
                                      //DO WORKING HERE
                                      return null;
                                  } 
                               }
       };
      

      这将允许您使用类似于以下的语法运行这些匿名函数:

      var output = actions["runproc"](inputparam);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-22
        • 2013-05-06
        相关资源
        最近更新 更多