Jak utworzyć słownik, w którym mogę przechowywać funkcje?
Dzięki.
Mam około 30+ funkcji, które mogą być wykonane przez użytkownika. Chcę móc wykonać tę funkcję w ten sposób:
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();
}
}
}
Jednak sygnatura funkcji nie zawsze jest taka sama, przez co ma różną liczbę argumentów.
Odpowiedzi:
Lubię to:
Dictionary<int, Func<string, bool>>
Pozwala to na przechowywanie funkcji, które pobierają parametr łańcuchowy i zwracają wartość logiczną.
dico[5] = foo => foo == "Bar";
Lub jeśli funkcja nie jest anonimowa:
dico[5] = Foo;
gdzie Foo jest zdefiniowane w ten sposób:
public bool Foo(string bar) { ... }
AKTUALIZACJA:
Po obejrzeniu aktualizacji wydaje się, że nie znasz z góry podpisu funkcji, którą chcesz wywołać. W .NET, aby wywołać funkcję, musisz przekazać wszystkie argumenty, a jeśli nie wiesz, jakie będą argumenty, jedynym sposobem na osiągnięcie tego jest odbicie.
A oto inna alternatywa:
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; } }
Przy takim podejściu nadal musisz znać liczbę i typ parametrów, które należy przekazać do każdej funkcji w odpowiednim indeksie słownika, w przeciwnym razie wystąpi błąd w czasie wykonywania. A jeśli twoje funkcje nie mają wartości zwracanych, użyj
System.Action<>
zamiastSystem.Func<>
.źródło
Dictionary<int, Delegate>
.Zacznijmy od kilku funkcji zdefiniowanych w ten sposób:
private object Function1() { return null; } private object Function2(object arg1) { return null; } private object Function3(object arg1, object arg3) { return null; }
Naprawdę masz do dyspozycji 2 realne opcje:
1) Zachowaj bezpieczeństwo typów, prosząc klientów o bezpośrednie wywoływanie Twojej funkcji.
To chyba najlepsze rozwiązanie, chyba że masz bardzo dobre powody, by oderwać się od tego modelu.
Kiedy mówisz o chęci przechwytywania wywołań funkcji, wydaje mi się, że próbujesz ponownie wymyślić funkcje wirtualne. Istnieje mnóstwo sposobów na uzyskanie tego rodzaju funkcjonalności po wyjęciu z pudełka, takich jak dziedziczenie z klasy bazowej i nadpisywanie jej funkcji.
Wydaje mi się, że potrzebujesz klasy, która jest bardziej opakowaniem niż wyprowadzona instancja klasy bazowej, więc zrób coś takiego:
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) LUB zmapuj wejście funkcji do wspólnego interfejsu.
Może to zadziałać, jeśli wszystkie funkcje są ze sobą powiązane. Na przykład, jeśli piszesz grę, a wszystkie funkcje robią coś z jakąś częścią ekwipunku gracza lub gracza. Skończyłbyś z czymś takim:
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); } }
źródło
object
podobne do tego, które zostanie przekazane do nowego wątku.Hej, mam nadzieję, że to pomoże. Z jakiego języka pochodzisz?
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); } }
źródło
Dlaczego nie użyć
params object[] list
dla parametrów metod i przeprowadzić walidację wewnątrz metod (lub logiki wywoływania), pozwoli to na zmienną liczbę parametrów.źródło
Poniższy scenariusz umożliwiłby użycie słownika elementów do przesłania jako parametrów wejściowych i uzyskania tego samego, co parametry wyjściowe.
Najpierw dodaj następujący wiersz u góry:
using TFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Collections.Generic.IDictionary<string, object>>;
Następnie w klasie zdefiniuj słownik w następujący sposób:
private Dictionary<String, TFunc> actions = new Dictionary<String, TFunc>(){ {"getmultipledata", (input) => { //DO WORKING HERE return null; } }, {"runproc", (input) => { //DO WORKING HERE return null; } } };
Umożliwiłoby to uruchamianie tych anonimowych funkcji ze składnią podobną do tej:
var output = actions["runproc"](inputparam);
źródło
Zdefiniuj słownik i dodaj odwołanie do funkcji jako wartość, używając
System.Action
jako typu: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 } }
Następnie wywołaj go za pomocą:
Actions.myActions["myKey"]();
źródło