C # İşlevleri bir sözlükte saklayın


93

Fonksiyonları saklayabileceğim bir Sözlük nasıl oluşturabilirim?

Teşekkürler.

Kullanıcı tarafından yürütülebilecek yaklaşık 30'dan fazla işleve sahibim. İşlevi bu şekilde yürütebilmek istiyorum:

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

Bununla birlikte, işlev imzası her zaman aynı değildir, bu nedenle farklı miktarda argüman içerir.


7
Bu harika bir deyimdir - kötü anahtar ifadelerinin yerini alabilir.
Hamish Grubijan

1
Bu sadece kendi başıma kötü yazılmış bir örnekti. Bunu yapacak bir Sözlük oluşturmayı bilmiyorum çünkü işlev imzası her zaman farklıdır.
chi

1
@HamishGrubijan, bunu bir switch ifadesini değiştirmek için yaparsanız, tüm derleme zamanı optimizasyonunu ve netliğini atmış olursunuz. Yani, deyimden daha aptalca olduğunu söyleyebilirim. İşlevselliği, çalışma zamanında değişebilecek bir şekilde dinamik olarak eşlemek istiyorsanız, bu yararlı olabilir.
Jodrell

14
@Jodrell, A) Aptal, yapıcı olmayan güçlü bir kelimedir, B) Açıklık bir bakanın gözündedir. Pek çok çirkin anahtar ifadesi gördüm. C) Derleme zamanı optimizasyonu ... Bu iş parçacığı üzerindeki Zooba, stackoverflow.com/questions/505454/… ' nin tersini savunuyor. Switch ifadeleri O (log N) ise, hızın bir fark yaratması için 100'den fazla durum içermesi gerekir. . Elbette bu okunabilir değil. Belki bir switch deyimi, mükemmel bir hash işlevini kullanabilir, ancak yalnızca küçük bir durum sayısı için. .Net kullanıyorsanız, mikrosaniyelerden fazla endişelenmezsiniz.
Hamish Grubijan

4
@Jodrell, (devam) Donanımınızdan en iyi şekilde yararlanmak istiyorsanız, bu sırayla ASM, C veya C ++ kullanırsınız. .Net'te sözlük araması sizi öldürmez. Temsilcilerin çeşitli imzaları - basit bir sözlük yaklaşımı kullanmaya karşı koyduğunuz en güçlü nokta budur.
Hamish Grubijan

Yanıtlar:


121

Bunun gibi:

Dictionary<int, Func<string, bool>>

Bu, bir dize parametresi alan ve boolean döndüren işlevleri saklamanıza izin verir.

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

Veya işlev anonim değilse:

dico[5] = Foo;

Foo şu şekilde tanımlanır:

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

GÜNCELLEME:

Güncellemenizi gördükten sonra, çağırmak istediğiniz işlevin imzasını önceden bilmediğiniz görünüyor. .

Ve işte başka bir alternatif:

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

Bu yaklaşımla, sözlüğün karşılık gelen dizinindeki her bir işleve aktarılması gereken parametrelerin sayısını ve türünü bilmeniz gerekir, aksi takdirde çalışma zamanı hatası alırsınız. Ve işlevlerinizin dönüş değerleri yoksa, System.Action<>yerine kullanın System.Func<>.


Anlıyorum. Sanırım biraz düşünmek hakkında okumak zorunda kalacağım, yardımın için minnettarım.
chi

@chi, son güncellememe bakın. İle bir örnek ekledim Dictionary<int, Delegate>.
Darin Dimitrov

3
Olumsuz oy vermeyeceğim, ancak bu tür bir uygulamanın çok iyi nedenleri olmayan bir anti-model olduğunu söylemem gerekecek . OP istemcilerin işleve tüm bağımsız değişkenleri iletmesini bekliyorsa, o zaman neden ilk etapta bir işlev tablosu olsun? Neden müşteri dinamik çağrıların büyüsü olmadan işlevleri basitçe çağırmasın ?
Juliet

1
@ Juliet, sana katılıyorum, bunun iyi bir şey olduğunu asla söylemedim. Bu arada cevabımda, sözlüğün karşılık gelen dizininde her bir işleve aktarılması gereken parametrelerin sayısını ve türünü bilmemiz gerektiği gerçeğini vurguladım. Bu durumda, fonksiyonu doğrudan çağırabileceğimiz için muhtemelen bir hashtable'a bile ihtiyacımız olmadığına işaret ederek kesinlikle haklısınız.
Darin Dimitrov

1
Herhangi bir argüman almayan ve dönüş değeri olmayan bir işlevi depolamam gerekirse ne olur?
DataGreed

8

Bununla birlikte, işlev imzası her zaman aynı değildir, bu nedenle farklı miktarda argüman içerir.

Bunun gibi tanımlanan birkaç işlevle başlayalım:

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

Gerçekten elinizin altında 2 geçerli seçeneğiniz var:

1) İstemcilerin işlevinizi doğrudan aramasını sağlayarak tür güvenliğini sağlayın.

Bu modelden kopmak için çok iyi nedenleriniz yoksa, muhtemelen en iyi çözüm budur .

İşlev çağrılarını durdurmaktan bahsettiğinizde, bana sanal işlevleri yeniden icat etmeye çalışıyormuşsunuz gibi geliyor. Bir temel sınıftan miras almak ve işlevlerini geçersiz kılmak gibi, bu tür bir işlevselliği kutudan çıkarmanın birçok yolu vardır.

Bana öyle geliyor ki , temel sınıfın türetilmiş bir örneğinden çok bir sarmalayıcı olan bir sınıf istiyorsun , öyleyse şöyle bir şey yap:

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) VEYA işlevlerinizin girişini ortak bir arayüzle eşleyin.

Tüm işlevleriniz birbiriyle ilişkiliyse bu işe yarayabilir. Örneğin, bir oyun yazıyorsanız ve tüm işlevler, oyuncunun veya oyuncunun envanterinin bir kısmına bir şeyler yapıyor. Bunun gibi bir şeye sahip olacaksın:

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

Benim varsayımım, objectyeni bir İş Parçacığına aktarılacak olan gibi olacaktır.
Scott Fraley

2

Sözlüğü tanımlayın ve işlev referansını tür olarak kullanarak değer olarak ekleyin 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
    }
}

Sonra şununla çağırın:

Actions.myActions["myKey"]();

Şimdiye kadar vakaların% 99'unda olağan cevap.
Fattie

1

Hey, umarım bu yardımcı olur. Hangi dilden geliyorsun

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

Neden params object[] listyöntem parametreleri için kullanmıyorsunuz ve yöntemleriniz (veya çağrı mantığı) içinde bazı doğrulamalar yapmıyorsunuz, değişken sayıda parametreye izin verecektir.


1

Aşağıdaki senaryo, girdi parametreleri olarak göndermek ve çıktı parametreleriyle aynı şeyi elde etmek için bir öğeler sözlüğü kullanmanıza izin verecektir.

İlk önce aşağıdaki satırı en üste ekleyin:

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

Ardından, sınıfınızın içinde sözlüğü aşağıdaki gibi tanımlayın:

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

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

Bu, bu anonim işlevleri şuna benzer bir sözdizimi ile çalıştırmanıza izin verir:

var output = actions["runproc"](inputparam);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.