2010-01-20 4 views
10

Ich bin ein C++ - Entwickler mit Signalen & Slots in C++, die zu mir scheint analog zu Delegaten in C# verwendet werden. Ich bin nicht mehr auf der Suche nach der Funktionalität von "bind" und habe das Gefühl, dass ich etwas verpassen muss.(Wie) ist es möglich, eine Methode zu binden/erneut zu binden, um mit einem Delegaten einer anderen Signatur zu arbeiten?

Ich fühle mich so etwas wie das Folgende, was in C++ möglich ist sollte in C# mit Delegierten möglich sein. Hier einige psudo-Code für das, was ich in c tun würde ++:

Slot<void> someCallback; 

int foo(int i) 
{ 
    std::cout << "Value: " << i << "\n"; 
    return i; 
} 

int main() 
{ 
    int i = 0; 
    Slot<int> someCallback = bind(fun_ptr(foo), i); 
    ++i; // added to show that late evaluation would be a non-trivial difference 
    int result = someCallback(); 
    assert(result == 0); 
    return 0; 
} 

Leider habe ich nicht in der Lage gewesen, eine Bezugnahme auf die Bindung/Rebinding in Bezug auf C# Delegierten zu finden. Fehle ich etwas? Gibt es eine radikal andere Möglichkeit, dies in C# zu tun?

Antwort

13

In C# wir etwas tun, wie folgt aus:

class Program { 
    static Action Curry<T>(Action<T> action, T parameter) { 
     return() => action(parameter); 
    } 

    static void Foo(int i) { 
     Console.WriteLine("Value: {0}", i); 
    } 
    static void Main(string[] args) { 
     Action curried = Curry(Foo, 5); 
     curried(); 
    } 
} 

Klar, dass die Methode Foo auf Ihre Methode entspricht Foo, nur mit den entsprechenden Anrufe zu Console.WriteLine statt std::cout.

Als nächstes deklarieren wir eine Methode Curry, die eine Action<T> akzeptiert und eine Action zurückgibt. Im Allgemeinen ist Action<T> ein Delegat, der einen einzelnen Parameter vom Typ T akzeptiert und void zurückgibt. Insbesondere ist Foo ein Action<int>, weil es einen Parameter des Typs int akzeptiert und void zurückgibt. Wie für den Rückgabetyp Curry wird es als Action deklariert. Ein Action ist ein Delegat, der keine Parameter hat und void zurückgibt.

Die Definition von Curry ist ziemlich interessant. Wir definieren eine Aktion mit einem Lambda-Ausdruck, der eine ganz besondere Form eines anonymen Delegierten darstellt. Effektiv

() => action(parameter) 

sagt, dass die void Parameter action bei parameter ausgewertet abgebildet wird.

schließlich in Main wir deklarieren eine Instanz von Actioncurried benannt, das Ergebnis ist Curry zu Foo mit dem Parameter 5 Anwendung. Dies spielt die gleiche Rolle wie bind(fun_ptr(foo), 5) in Ihrem C++ Beispiel.

Zuletzt rufen wir den neu gebildeten Delegaten curried über die Syntax curried() auf. Dies ist wie someCallback() in Ihrem Beispiel.

Die phantastische Bezeichnung dafür ist currying.

Als ein interessantes Beispiel, sollten Sie Folgendes beachten:

class Program { 
    static Func<TArg, TResult> Curry<TArg, TResult>(
     Func<TArg, TArg, TResult> func, 
     TArg arg1 
    ) { 
     return arg => func(arg1, arg); 
    } 

    static int Add(int x, int y) { 
     return x + y; 
    } 

    static void Main(string[] args) { 
     Func<int, int> addFive = Curry<int, int>(Add, 5); 
     Console.WriteLine(addFive(7)); 
    } 
} 

Hier stellen wir ein Verfahren Curry deklarieren, die einen Delegierten akzeptiert (Func<TArg, TArg, TResult>, die zwei Parameter des gleichen Typs akzeptiert TArg und gibt einen Wert von einem anderen Typ TResult und einen Parameter vom Typ TArg und gibt einen Vertreter, die einen einzelnen Parameter des Typs TResultTArg und gibt einen Wert des Typs akzeptiert (Func<TArg, TResult>).

Als Test deklarieren wir dann eine Methode Add, die zwei Parameter vom Typ int akzeptiert und einen Parameter vom Typ int (a Func<int, int, int>) zurückgibt. Dann in Main instanziieren wir einen neuen Delegaten mit dem Namen addFive, der wie eine Methode wirkt, die seinem Eingabeparameter fünf hinzufügt. Also

Console.WriteLine(addFive(7)); 

druckt 12 auf der Konsole.

+0

Danke für Ihre ausführliche Antwort. Ich habe das andere als die akzeptierte Antwort markiert, weil es prägnanter ist, obwohl es einige wichtige Details fehlen, die Ihre eingeschlossen haben. – Catskul

+0

Sicher. Ich hoffe nur, dass die zusätzliche Farbe hilft. :-) – jason

+0

Extrem nützliche Antwort, das. Einführung eines brillanten Konzepts, Currys, auf leicht verständliche Art und Weise. Wird dies definitiv zu meiner mentalen Toolbox hinzufügen. –

4

Versuchen Sie, die folgenden

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    public static void Main() { 
    Action someCallback =() => foo(5); 
    someCallback(); 
    } 
} 

Oder etwas noch näher an das Gegenstück ++ C

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    static Action bind<T>(Action<T> action, T value) { 
    return() => action(value); 
    } 
    public static void Main() { 
    Action someCallback = bind(foo, 5); 
    someCallback(); 
    } 
} 

Erklärung. Was hier passiert ist, dass ich einen neuen Delegaten mit Hilfe eines Lambda-Ausdrucks erstelle. Das Lambda ist der Ausdruck beginnend mit () =>. In diesem Fall wird ein Delegat erstellt, der keine Argumente akzeptiert und keinen Wert erzeugt. Es ist kompatibel mit dem Typ Action.

+0

Woah. Interessant. Was sehe ich hier? – Catskul

+2

Er erstellt einen neuen Delegaten mit einem Lambda-Ausdruck. Der Delegat ruft "foo (5)" auf. Es ist so, als würde man im Handumdrehen eine neue Methode erstellen, um foo (5) für Sie aufzurufen und sie dann dem SomeCallback-Delegaten zuzuweisen. –

+0

'Aktion ' ist ein Delegat, der vom Framework bereitgestellt wird. Es ist eine Funktion, die einen Parameter (vom Typ T) und nichts (void) zurückgibt. Er weist diesem Delegaten eine anonyme Funktion zu (die er "someCallback" genannt hat). Die leeren Parens zeigen an, dass es keine Argumente benötigt, der Ausdruck nach dem '=>' ist der Körper der Funktion. – Sapph

Verwandte Themen