2015-09-09 8 views
5

Hier geht es um die Kommunikation zwischen C++ (gemeinsam genutzter Code verschiedener Plattformen) mit C# (Windows Universal App). Wir wissen, dass der folgende Funktionsaufruf von C++ bis C# erfolgt.Wie warten wir auf C# asynchrone Delegatenfunktion in C++/CX?

C#

class FooCS 
{ 
    FooCS() 
    { 
     FooC c = new ref FooC(); 
     c.m_GetSomeValueEvent = GetSomeValueEvent; 
     // Some other stuff ... 
    } 

    string GetSomeValueEvent() 
    { 
     // Some other stuff ... 
     return "Hello World"; 
    } 
} 

C++

public delegate Platform::String GetSomeValueEventHandler(); 

class FooC 
{ 
    public: 
    event GetSomeValueEventHandler^ m_GetSomeValueEvent; 
    void someFunction(){ 
     Platform::String^ str = m_GetSomeValueEvent(); 
     // Some other stuff ... 
    } 
} 

oben Das ist sehr einfach. Aber das Problem ist, dass diese Zeichenfolge GetSomeValueEvent() in C# eine schwere Aufgabe wie Lesen von Daten aus der Datenbank, und ich muss es async machen.

async Task<string> GetSomeValueEvent() { 
     // Some other stuff ... 
     return "Hello World"; 
    } 

Jetzt kommt die Frage: Wie kann ich meine C++ Code Warten auf die Rückkehr dieses Delegatfunktion? In einem anderen Wort, auf was sollte die folgende Unterschrift geändert werden?

public delegate Platform::String GetSomeValueEventHandler(); 

Ich habe googeln und kann nicht die richtige Terminologie für dieses Problem finden. Wenn Sie sicher sind, dass es unmöglich ist, wäre zumindest schön zu wissen.


Schließlich, ist es das, was wir tun:

string GetSomeValueEvent() { 
     // Convert a async method to non-async one 
     var val = someAsyncMethod().Result; 
     // Some other stuff ... 
     return "Hello World"; 
    } 

Sie müssen sicherstellen, dass GetSomeValueEvent nicht auf dem Haupt-Thread-Deadlock zu verhindern läuft.

Antwort

3

UPD: Einen Wert von einem Ereignis zurückzugeben ist eine wirklich schlechte Idee. Betrachten Sie die anderen Optionen als eine Art beobachtbares Muster, aber mit einem einzelnen Beobachter.

Wie @RaymondChen erwähnt, ist es für asynchrone Ereignisse besser, das Deferral-Muster zu verwenden. Es ist ein häufig verwendetes Muster in Windows Runtime.

Fügen Sie die Methode GetDeferral() zu Ihren Ereignisargumenten hinzu, die ein spezielles Verzögerungsobjekt zurückgeben. Dieses Objekt muss über die Complete()-Methode verfügen, die Ihren Code darüber informiert, dass ein asynchroner Vorgang abgeschlossen wurde.

============================================== ==========================

Das Schlüsselwort async ändert eigentlich keinen Funktionsprototyp. Sie müssen also nur den Rückgabewert in Ihrem Delegaten ändern, damit er mit dem ursprünglichen übereinstimmt (Task<string>).

Aber es gibt einen Haken: Windows Runtime hat nicht den Task<> Typ, es ist ein Teil von TPL, die eine .NET-Bibliothek ist. Windows Runtime verwendet stattdessen IAsyncOperation<> und IAsyncAction, um asynchrone Vorgänge darzustellen.

Also hier ist, was Sie tun müssen:

  1. Delegierter Unterschrift Alter:

    public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler(); 
    
  2. Verwenden Sie die AsAsyncOperation() Erweiterungsmethode Task<> zu IAsyncOperation<> konvertieren:

    async Task<string> GetSomeValueEvent() 
    { 
        // Some other stuff ... 
        return "Hello World"; 
    } 
    
    IAsyncOperation<string> GetSomeValueEventWrapper() 
    { 
        return GetSomeValueEvent().AsAsyncOperation(); 
    } 
    
    FooCS() 
    { 
        FooC c = new ref FooC(); 
        c.m_GetSomeValueEvent = GetSomeValueEventWrapper; 
        // Some other stuff ... 
    } 
    
+0

Dieser Fehler tritt auf, wenn Sie zwei Ereignisteilnehmer haben, da nur einer von ihnen für den Rückgabewert verwendet wird. Die Klasse wartet auf den Abschluss nur einer Teilnehmeraufgabe. Besser ist es, das Deferral-Muster zu verwenden. –

+0

@ RaymondChen Ich stimme zu, ich hätte es erwähnen sollen. Aber die Rückgabe eines Wertes aus einem Event-Handler ist eine wirklich schlechte Idee und ich weiß ehrlich gesagt nicht, wie die Verwendung des Deferral-Patterns das auflösen wird. –

+0

Das Windows-Laufzeitmuster soll einen 'EventArgs'-Parameter übergeben, der eine' GetDeferral'-Methode und einen Mechanismus zum Melden von Ergebnissen aufweist. Jeder Delegat nimmt eine Zurückstellung, führt seine asynchrone Arbeit aus, meldet das Ergebnis über den Berichterstellungsmechanismus (möglicherweise eine "SetResult" -Methode oder durch Anhängen des Ergebnisses an eine Sammlung) und schließt dann die Verzögerung ab. Wenn die letzte Verzögerung abgeschlossen ist, überprüft der Aufrufer alle berichteten Ergebnisse. –

0

Was würde ich tun, ist eine C++ Äquivalent verwenden:

var result = GetSomeValueEvent().GetAwaiter().GetResult(); 

, was das für Sie tun nicht nur Ergebnis holen, sondern auch alle Ausnahmen Passthrough in dieser Aufgabe geschieht.

Verwandte Themen