2010-11-20 15 views
70

Come si crea un dizionario in cui è possibile memorizzare le funzioni?C# Memorizza le funzioni in un dizionario

Grazie.

Ho circa 30+ funzioni che possono essere eseguite dall'utente. Voglio essere in grado di eseguire la funzione in questo modo:

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

Tuttavia, la firma funzione non è sempre la stessa, avendo così diverse quantità di argomenti.

+2

Questo è un ottimo idioma - può sostituire le dichiarazioni di commutazione brutto. –

+0

La funzione di esempio ha parametri, tuttavia, quando si richiama la funzione memorizzata, la si richiama senza alcun argomento. Gli argomenti vengono risolti quando la funzione è archiviata? –

+0

Quello era solo un esempio scritto male da solo. Non so creare un dizionario che lo faccia, perché la firma della funzione è sempre diversa. – chi

risposta

81

così: si

Dictionary<int, Func<string, bool>> 

Questo permette di memorizzare le funzioni che accettano un parametro di stringa e booleano ritorno.

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

Oppure, se la funzione non è anonimo:

dico[5] = Foo; 

dove Foo è definito in questo modo:

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

UPDATE:

Dopo aver visto l'aggiornamento è sembra che tu non lo sappia in anticipo la firma della funzione che desideri invocare. In .NET per invocare una funzione è necessario passare tutti gli argomenti e se non si conoscono gli argomenti che saranno l'unico modo per ottenere ciò è attraverso la riflessione.

Ed ecco un'altra 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 questo approccio è comunque necessario conoscere il numero e il tipo di parametri che devono essere passati ad ogni funzione al corrispondente indice del dizionario o otterrete runtime errore. E se le tue funzioni non hanno valori di ritorno usa System.Action<> invece di System.Func<>.

+0

Vedo. Credo che dovrò passare un po 'di tempo a leggere sul riflesso, quindi, apprezzare l'aiuto. – chi

+0

@chi, guarda il mio ultimo aggiornamento. Ho aggiunto un esempio con 'Dictionary '. –

+0

Inoltre, con il modello 'Delegate()', non puoi combinare i valori 'dico':' dico [1] + = new Func <...> (Func3) 'così ora dico [1] esegue Func1 e Func3? – IAbstract

9

Tuttavia, la firma funzione non sempre la stessa, avendo così differente quantità di argomenti.

Cominciamo con alcune funzioni definite in questo modo:

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

È veramente 2 opzioni praticabili a vostra disposizione:

1) Mantenere tipo di sicurezza da avere clienti chiamare la funzione direttamente.

Questa è probabilmente la soluzione migliore, a meno che non si abbia molto buoni motivi per rompere con questo modello.

Quando parli di voler intercettare le chiamate di funzione, mi sembra che tu stia cercando di reinventare le funzioni virtuali. C'è un carico di modi per ottenere questo tipo di funzionalità fuori dalla scatola, come ereditare da una classe base e sovrascrivere le sue funzioni.

Sembra a me come si vuole una classe che è più di un involucro di un'istanza derivata di una classe base, in modo da fare qualcosa di simile:

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 una mappa all'ingresso di le tue funzioni a un'interfaccia comune.

Questo potrebbe funzionare se tutte le funzioni sono correlate. Ad esempio, se stai scrivendo un gioco, e tutte le funzioni fanno qualcosa in qualche parte dell'inventario del giocatore o del giocatore. Si finirebbe con qualcosa di simile:

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

Cos'è 'State'? – MrHIDEn

+0

Lo stato è di classe? – Sandip

+0

La mia ipotesi sarebbe un 'oggetto' come quello che verrebbe passato a una nuova discussione. –

0

Ehi, spero che questo aiuti. Da che lingua vieni?

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

Perché non usare params object[] list per i parametri di metodo e di fare un po 'di validazione all'interno sia i vostri metodi (o logica chiamando), consentirà per un numero variabile di parametri.

0

Il seguente scenario consente di utilizzare un dizionario di elementi da inviare come parametri di input e ottenere lo stesso valore dei parametri di output.

Prima aggiungere la seguente riga in alto:

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

poi dentro la classe, definire il dizionario come segue:

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

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

Questo permetterebbe di eseguire queste funzioni anonime con una sintassi simile a questo:

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