2015-09-09 6 views
5

Si tratta della comunicazione tra C++ (codice condiviso di piattaforme diverse) con C# (Windows Universal App). Sappiamo che quanto segue è come facciamo una chiamata di funzione da C++ a C#.Come attendiamo la funzione di delegato asincrona C# 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 ... 
    } 
} 

Quanto sopra è molto semplice. Ma il problema è che questa stringa GetSomeValueEvent() in C# esegue alcune attività pesanti come la lettura dei dati dal database e devo renderlo async.

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

Ora qui viene la domanda: come faccio a fare il mio C++ codice di attesa per il ritorno di questa funzione delegata? In un'altra parola, a cosa dovrebbe essere modificata la seguente firma?

public delegate Platform::String GetSomeValueEventHandler(); 

Sono stato google e non riesco a trovare la terminologia giusta per questo problema. Se sai per certo che è impossibile, sarebbe almeno bello da sapere.


Alla fine, questo è ciò che facciamo:

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

È necessario assicurarsi GetSomeValueEvent non è in esecuzione sul thread principale per evitare stallo.

risposta

3

UPD: restituire un valore da un evento è un'idea davvero CATTIVO. Considera le altre opzioni come una sorta di modello osservabile ma con un singolo osservatore.

Come menzionato @RaymondChen, per gli eventi asincroni è preferibile utilizzare il modello di differimento. È un pattern comunemente usato in Windows Runtime.

Aggiungi il metodo GetDeferral() al tuo evento arg che restituisce uno speciale oggetto di differimento. Questo oggetto deve avere il metodo Complete() che informa il tuo codice che è stata completata un'operazione asincrona.

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

La parola chiave async in realtà non cambia un prototipo di funzione. Quindi è sufficiente modificare il valore restituito nel delegato per farlo corrispondere a quello originale (Task<string>).

Ma c'è un problema: Windows Runtime non ha il tipo Task<>, è una parte di TPL che è una libreria .NET. Windows Runtime utilizza invece IAsyncOperation<> e IAsyncAction per rappresentare operazioni asincrone.

Quindi, ecco cosa devi fare:

  1. Modificare la firma del delegato:

    public delegate Windows::Foundation::IAsyncOperation<Platform::String> GetSomeValueEventHandler(); 
    
  2. Utilizzare il metodo AsAsyncOperation() estensione per convertire Task<> a IAsyncOperation<>:

    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

Questo errore si interrompe quando si dispone di due sottoscrittori di eventi, poiché solo uno di essi verrà utilizzato per il valore restituito. La classe aspetterà che venga completata una sola attività di sottoscrizione. È meglio usare il modello di differimento. –

+0

@RaymondChen Sono d'accordo, avrei dovuto dirlo. Ma restituire un valore da un gestore di eventi è una pessima idea e onestamente non so come risolvere il problema con il rinvio del differimento. –

+0

Il modello di Windows Runtime passa un parametro 'EventArgs' che ha un metodo' GetDeferral' e un meccanismo per la segnalazione dei risultati. Ogni delegato prende un differimento, esegue il suo lavoro asincrono, riporta il risultato tramite il meccanismo di segnalazione (forse un metodo 'SetResult', o aggiungendo il risultato a una raccolta), quindi completando il differimento. Al termine dell'ultimo differimento, il chiamante controlla tutti i risultati riportati. –

0

Quello che vorrei fare è usare un C++ equivalente di:

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

cosa che farà per voi non è solo prendere risultato, ma passthrough anche tutte le eccezioni che accadono in questo compito.

Problemi correlati