Pregunta C # Almacenar funciones en un diccionario


¿Cómo creo un diccionario donde puedo almacenar funciones?

Gracias.

Tengo más de 30 funciones que pueden ser ejecutadas por el usuario. Quiero poder ejecutar la función de esta manera:

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

Sin embargo, la firma de la función no es siempre la misma, por lo que tiene diferentes cantidades de argumentos.


74
2017-11-20 15:42


origen


Respuestas:


Me gusta esto:

Dictionary<int, Func<string, bool>>

Esto le permite almacenar funciones que toman un parámetro de cadena y devuelven booleano.

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

O si la función no es anónima:

dico[5] = Foo;

donde Foo se define así:

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

ACTUALIZAR:

Después de ver su actualización, parece que no sabe de antemano la firma de la función que desea invocar. En .NET para invocar una función necesita pasar todos los argumentos y si no sabe cuáles serán los argumentos, la única forma de lograrlo es a través de la reflexión.

Y aquí hay otra alternativa:

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

Con este enfoque, aún necesita saber la cantidad y el tipo de parámetros que deben pasarse a cada función en el índice correspondiente del diccionario o obtendrá un error de tiempo de ejecución. Y si sus funciones no tienen valores de retorno, use System.Action<> en lugar de System.Func<>.


89
2017-11-20 15:43



Sin embargo, la firma de la función no es   siempre lo mismo, teniendo así diferentes   cantidad de argumentos.

Comencemos con algunas funciones definidas así:

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

Realmente tienes 2 opciones viables a tu disposición:

1) Mantenga la seguridad de tipo al hacer que los clientes llamen a su función directamente.

Esta es probablemente la mejor solución, a menos que tenga muy buenas razones para romper con este modelo.

Cuando hablas de querer interceptar llamadas de función, me parece que intentas reinventar funciones virtuales. Hay una gran cantidad de maneras de obtener este tipo de funcionalidad de la caja, como heredar de una clase base y anular sus funciones.

Me parece que quieres una clase que sea más de envoltura que una instancia derivada de una clase base, entonces haz algo como esto:

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) O mapee la entrada de sus funciones a una interfaz común.

Esto podría funcionar si todas sus funciones están relacionadas. Por ejemplo, si está escribiendo un juego, y todas las funciones le hacen algo a alguna parte del jugador o del inventario del jugador. Terminas con algo como esto:

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

9
2017-11-20 16:58



Por qué no usar params object[] list para los parámetros del método y hacer alguna validación dentro de sus métodos (o lógica de llamada), permitirá un número variable de parámetros.


1
2017-07-01 10:21



Oye, espero que esto ayude. ¿De qué idioma vienes?

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

0
2017-11-20 17:00



El siguiente escenario le permitiría usar un diccionario de elementos para enviar como parámetros de entrada y obtener el mismo resultado que los parámetros de salida.

Primero agrega la siguiente línea en la parte superior:

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

Luego, dentro de tu clase, define el diccionario de la siguiente manera:

     private Dictionary<String, TFunc> actions = new Dictionary<String, TFunc>(){

                        {"getmultipledata", (input) => 
                            {
                                //DO WORKING HERE
                                return null;
                            } 
                         }, 
                         {"runproc", (input) => 
                            {
                                //DO WORKING HERE
                                return null;
                            } 
                         }
 };

Esto le permitiría ejecutar estas funciones anónimas con una sintaxis similar a esta:

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

0
2018-02-19 03:22



Defina el diccionario y agregue la referencia de la función como el valor, usando System.Actioncomo el tipo:

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

Luego invocarlo con:

Actions.myActions["myKey"]();

0
2018-05-27 04:11