2010-11-20 2 views
70

Как создать словарь, в котором я могу хранить функции?C# Функции магазина в словаре

Спасибо.

У меня есть около 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(); 
      } 
     } 
    } 

Однако функция подписи не всегда то же самое, что имея различное количество аргументов.

+2

Это отличная идиома - может заменить неприятные операторы switch. –

+0

В вашем примере функция имеет параметры, однако при вызове хранимой функции вы вызываете ее без каких-либо аргументов. Являются ли аргументы фиксированными при сохранении функции? –

+0

Это был просто плохо написанный пример. Я не знаю, как создать словарь, который бы это сделал, потому что сигнатура функции всегда отличается. – chi

ответ

81

Как это:

Dictionary<int, Func<string, bool>> 

Это позволяет сохранять функции, которые принимают параметр строки и вернуть логическое значение.

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

Или, если функция не является анонимным:

dico[5] = Foo; 

где Foo определяется следующим образом:

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

UPDATE:

После просмотра вашего обновления это кажется, что вы не знаете заранее подпись функции, которую вы хотите вызвать. В .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<> вместо System.Func<>.

+0

Я вижу. Наверное, мне придется потратить некоторое время на размышления, а потом, оцените помощь. – chi

+0

@chi, см. Мое последнее обновление. Я добавил пример с словарем '. –

+0

Кроме того, с помощью модели 'Delegate()' вы не можете объединить значения 'dico':' dico [1] + = new Func <...> (Func3) 'так что теперь dico [1] выполняет Func1, а затем Func3? – IAbstract

9

Однако функция подписи не всегда то же самое, что имея различное количество аргументов.

Давайте начнем с нескольких функций, определенных следующим образом:

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

Вы действительно есть 2 жизнеспособных вариантов в вашем распоряжении:

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

Что такое 'State'? – MrHIDEn

+0

Состояние - класс? – Sandip

+0

Мое предположение было бы «объектом», подобным тому, которое будет передано в новый поток. –

0

Эй, надеюсь, это поможет. На каком языке вы пришли?

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

Почему бы не использовать params object[] list для параметров методы и сделать некоторые проверки внутри либо ваших методов (или вызывающей логики), что позволит с переменным числом параметров.

0

Следующий сценарий позволит вам использовать словарь элементов для отправки в качестве входных параметров и получить то же, что и выходные параметры.

Сначала добавьте следующую строку в верхней части:

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); 
Смежные вопросы