2015-11-19 13 views
13

C'è una differenza tra l'uso di [In, Out] e l'uso di ref quando si passano i parametri da C# a C++?Qual è la differenza tra [In, Out] e ref quando si usa pinvoke in C#?

Ho trovato un paio di post SO diversi e alcune cose da MSDN che si avvicina alla mia domanda ma non risponde abbastanza. La mia ipotesi è che posso tranquillamente usare ref proprio come vorrei usare [In, Out], e che il marshaller non agirà in modo diverso. La mia preoccupazione è che agirà in modo diverso, e che C++ non sarà contento del passaggio della mia struttura C#. Ho visto entrambe le cose fatte nella base di codice su cui sto lavorando in ...

Qui ci sono i post che ho trovato e sono stati la lettura attraverso:

Are P/Invoke [In, Out] attributes optional for marshaling arrays? mi fa pensare che dovrei usare [ Dentro fuori].

Questi tre messaggi mi fanno pensare che dovrei usare [in, out], ma che posso usare rif invece e avrà lo stesso codice macchina. Questo mi fa pensare che ho torto, quindi mi chiedo qui.

+0

Puoi dare un esempio concreto per favore. Vuoi conoscere la differenza tra '[In, Out] ref int foo' e' ref int foo'? Così com'è, alla tua domanda mancano i dettagli, e credo che tu abbia bisogno di uno (o più) esempi concreti. –

+0

Qualcosa di simile, è: [DllImport ("MyDll.dll", CallingConvention = CallingConvention.Cdecl)] private static extern bool myFunction ([In, Out] SomeStruct [] aStruct; lo stesso che usare ref al posto di [In , Out]. – PerryC

+0

Inoltre, hai appena combinato [In, Out] e ref;)? – PerryC

risposta

14

L'utilizzo di ref o out è non arbitrario. Se il codice nativo richiede il pass-by-reference (un puntatore), allora deve utilizzare le parole chiave se il tipo di parametro è un tipo di valore. In modo che il jitter sappia generare un puntatore al valore. E tu devi ometterli se il tipo di parametro è un tipo di riferimento (classe), gli oggetti sono già puntatori sotto il cofano.

Gli attributi [In] e [Out] sono quindi necessari per risolvere l'ambiguità sui puntatori, non specificano il flusso di dati. [In] è sempre implicito dal marshaller di pinvoke, quindi non è necessario specificarlo esplicitamente. Ma tu devi usare [Out] se ti aspetti di vedere eventuali modifiche apportate dal codice nativo a una struct o un membro della classe nel tuo codice. Il marshaller pinvoke evita di copiare automaticamente per evitare le spese.

Un'ulteriore peculiarità è che [Out] non è spesso necessario. Accade quando il valore è blittable, una parola costosa che significa che il valore gestito o il layout dell'oggetto è identico al layout nativo. Il marshaller pinvoke può quindi prendere una scorciatoia, bloccare l'oggetto e passare un puntatore all'archivio oggetti gestiti. Vedrai inevitabilmente delle modifiche poiché il codice nativo sta modificando direttamente l'oggetto gestito.

Qualcosa che in generale vuoi fortemente perseguire, è molto efficiente. Aiuta dando il tipo all'attributo [StructLayout (LayoutKind.Sequential)], sopprime un'ottimizzazione che il CLR usa per riorganizzare i campi per ottenere l'oggetto più piccolo. E usando solo campi di tipi di valore semplici o buffer di dimensioni fisse, anche se spesso non hai questa scelta. Non utilizzare mai una bool, utilizzare invece byte. Non esiste un modo semplice per scoprire se un tipo è blittabile, a parte il fatto che non funziona correttamente o utilizzando il debugger e confrontare i valori del puntatore.

Basta essere espliciti e utilizzare sempre [Out] quando ne hai bisogno. Non costa nulla se si rivela che non è necessario. Ed è auto-documentante.E puoi star bene che funzionerà anche se l'architettura del codice nativo cambia.

+0

Sono abbastanza sicuro che i miei oggetti non siano intercettabili (anche se non sono positivo). Se ho qualcosa che è attualmente [In, Out], lo cambierei per usare solo [Out] essere un codice più pulito e comportarsi nello stesso modo? O sarebbe un codice più pulito lasciare l'attributo [In, Out] per mostrare l'intento bidirezionale? Grazie per la tua risposta. – PerryC

+2

Se l'intento è bidirezionale, descriverlo in questo modo, * do * usa [In, Out]. –

+0

Se il valore è blittabile e non un tipo di riferimento (in questo caso, una struttura in C#), utilizza ref invece di utilizzare [In, Out] lavoro dato che la struct è sequenziale e contiene solo tipi primitivi (int, float, Doppio)? Sarebbe lo stesso che usare [In, Out] per il marshaller perché in entrambi i casi potrebbe pinare l'oggetto in memoria? – PerryC

Problemi correlati