Функции хранилища С# в словаре
Как создать словарь, где я могу хранить функции?
Спасибо.
У меня есть около 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();
}
}
}
Однако сигнатура функции не всегда одинакова, поэтому имеет различное количество аргументов.
Ответы
Ответ 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<>
вместо System.Func<>
.
Ответ 2
Однако подпись функции не всегда одно и то же, количество аргументов.
Начнем с нескольких функций, определенных следующим образом:
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);
}
}
Ответ 3
Почему бы не использовать params object[] list
для параметров метода и выполнить некоторую проверку внутри ваших методов (или логики вызова), Это позволит использовать переменное количество параметров.
Ответ 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
Следующий сценарий позволит вам использовать словарь элементов для отправки в качестве входных параметров и получить то же, что и выходные параметры.
Сначала добавьте следующую строку вверху:
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);
Ответ 6
Определите словарь и добавьте ссылку на функцию в качестве значения, используя 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"]();