사전에 C # 저장소 함수


93

함수를 저장할 수있는 사전을 어떻게 만듭니 까?

감사.

사용자가 실행할 수있는 기능이 약 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();
            }
        }
    }

그러나 함수 시그니처가 항상 동일한 것은 아니므로 인수의 양이 다릅니다.


5
이것은 훌륭한 관용구입니다-불쾌한 switch 문을 대체 할 수 있습니다.
Hamish Grubijan 2010

예제 함수에는 매개 변수가 있지만 저장된 함수를 호출하면 인수없이 호출하는 것입니다. 함수가 저장 될 때 인수가 고정됩니까?
Tim Lloyd

1
@HamishGrubijan, switch 문을 교체하기 위해이 작업을 수행하면 모든 컴파일 시간 최적화 및 명확성이 삭제됩니다. 그래서 나는 그것이 관용구보다 더 바보라고 말하고 싶습니다. 런타임에 달라질 수있는 방식으로 기능을 동적으로 매핑하려는 경우 유용 할 수 있습니다.
Jodrell

12
@Jodrell, A) 바보는 건설적이지 않은 강력한 단어입니다. B) 명확성은 보는 사람의 눈에 있습니다. 나는 추악한 switch 문을 많이 보았다. C) 컴파일 시간 최적화 ...이 스레드의 Zooba는 반대의 경우 stackoverflow.com/questions/505454/… 스위치 문이 O (log N)이면 속도를 위해 100 개 이상의 케이스를 포함해야합니다 . 물론 읽을 수 없습니다. switch 문은 완벽한 해시 함수를 사용할 수 있지만 적은 수의 경우에만 사용할 수 있습니다. .Net을 사용하는 경우 마이크로 초를 초과하지 않습니다.
Hamish Grubijan 2013

4
@Jodrell, (계속) 하드웨어를 최대한 활용하려면 ASM, C 또는 C ++를 순서대로 사용합니다. .Net의 사전 조회는 당신을 죽이지 않습니다. 대리자의 다양한 서명-이것은 간단한 사전 접근 방식을 사용하여 만든 가장 강력한 포인트입니다.
Hamish Grubijan 2013

답변:


120

이렇게 :

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


내가 참조. 나는 그때 반성에 대해 읽는 데 시간을 할애해야 할 것 같습니다. 도움을 주셔서 감사합니다.
chi

@chi, 내 마지막 업데이트를 참조하십시오. 나는 Dictionary<int, Delegate>.
Darin Dimitrov

3
나는 찬성하지 않겠지 만, 이런 종류의 구현은 아주 좋은 이유 없이 반 패턴이라고 말해야 할 것입니다 . OP가 클라이언트가 모든 인수를 함수에 전달할 것으로 예상하는 경우 왜 처음에 함수 테이블이 있습니까? 클라이언트 가 동적 호출의 마법없이 단순히 함수 를 호출 하지 않는 이유 는 무엇입니까?
Juliet

1
@ 줄리엣, 나는 당신과 동의합니다, 나는 그것이 좋은 것이라고 말한 적이 없습니다. 그건 그렇고, 내 대답에서 나는 사전의 해당 색인에서 각 함수에 전달해야 할 매개 변수의 수와 유형을 여전히 알아야한다는 사실을 강조했습니다. 이 경우 함수를 직접 호출 할 수 있으므로 해시 테이블이 필요하지 않을 수도 있다는 점을 지적하면 절대적으로 정확합니다.
Darin Dimitrov

1
인수를 취하지 않고 반환 값이없는 함수를 저장해야하는 경우 어떻게합니까?
DataGreed

8

그러나 함수 시그니처가 항상 동일한 것은 아니므로 인수의 양이 다릅니다.

다음과 같이 정의 된 몇 가지 함수부터 시작하겠습니다.

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

당신은 당신의 처분에 실제로 두 가지 실행 가능한 옵션이 있습니다.

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

내 가정은 object새 스레드에 전달되는 것과 같습니다.
Scott Fraley 2017 년

1

이게 도움이되기를 바랍니다. 어떤 언어에서 왔습니까?

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

params object[] list메서드 매개 변수에 사용하지 않고 메서드 (또는 호출 논리) 내에서 일부 유효성 검사를 수행하면 가변 개수의 매개 변수가 허용됩니다.


1

다음 시나리오에서는 요소 사전을 사용하여 입력 매개 변수로 전송하고 출력 매개 변수와 동일하게 가져올 수 있습니다.

먼저 맨 위에 다음 행을 추가하십시오.

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

0

사전을 정의하고 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"]();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.