2012-05-09 8 views
6

Vedo un sacco di voci per fare questo in C#, ma nulla per C++Come si può ottenere un nome di proprietà come una stringa in C++ gestito

Ho una serie di immobili di alcuni gestiti codice C++ che viene utilizzato per passare i dati tra la porzione C# e la parte C++. Nel lato C#, la risposta presentata here funziona molto bene e mi piacerebbe fare qualcosa di simile con C++. Copia della soluzione contenuta nel link:

string NameOf<T>(Expression<Func<T>> expr) { 
    return ((MemberExpression) expr.Body).Member.Name; 
} 

var gmtList = new SelectList(repository.GetSystemTimeZones(), 
    NameOf(() => tz.Id), 
    NameOf(() => tz.DisplayName)); 

Il mio problema è che io non riesco a ottenere la sintassi corretta per la chiamata, in particolare, questa sezione:

() => tz.DisplayName 

io non riesco a trovare una risorsa online che spieghi come farei questo in C++, quindi se qualcuno ha esperienza o collegamenti, apprezzerei davvero qualsiasi aiuto.

risposta

6

Ho lottato con tutti quei lambda e gli spazi dei nomi di sistema :: Linq (compresa la libreria CLinq e altre cose) per un paio d'ore e poi ... Ho solo pensato: perché non usare una decisione puramente statica?

Abbiamo appena dichiariamo la

/// Convert X to "X", the classical preprocessing trick 
#define GetPropName(TheClassName, ThePropertyName) #ThePropertyName 

e poi possiamo fare

Console::WriteLine(GetPropName(TimeZone, Id)); 

per ottenere il "Id" stampato sullo schermo.

Sì ... Ora la parte interessante. Il tipo di sicurezza. Ho sentito che la tempesta di commenti è arrivata su questa soluzione ("NO! Non va bene, non controlla se ThePropertyName è nella classe!")

OK. La soluzione: produciamo codice senza senso usando una macro che usa ThePropertyName in un'istanza fittizia di TheClassName.

/// This macro will produce the compilation error if ThePropertyName is not in the class named TheClassName 
#define CheckForPropertyExistence(TheClassName, ThePropertyName) \ 
/* Create an array of Objects which will be converted to string and ignored*/ \ 
(gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString() 

/// We get the property name using the "dinosaur strategy" - good old macro concatenated with the empty string which in turn is formed in CheckFor() macro 
#define GetPropertyName(TheClassName, ThePropertyName) \ 
(gcnew System::String(#ThePropertyName)) + CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0) 

Ora possiamo dare l'esempio completo:

using namespace System; 

/// Sample class 
public ref class TheTimeZone 
{ 
public: 
    TheTimeZone() 
    { 
     _Id = 0; 
     _DisplayName = "tmp"; 
    } 

    property int Id 
    { 
    public: 
     int get() {return _Id;} 
     void set(int v) { _Id = v; } 
    } 

    property String^ DisplayName 
    { 
    public: 
     String^ get() { return _DisplayName; } 
     void set(String^ v) { _DisplayName = v; } 
    } 

private: 
    int _Id; 
    String^ _DisplayName; 
}; 

/// This macro will produce the error if ThePropertyName is not in the class named TheClassName 
#define CheckForPropertyExistence(TheClassName, ThePropertyName) \ 
/* Create an array of Objects which will be converted to string and ignored*/ \ 
(gcnew array<System::Object^> { (gcnew TheClassName())->ThePropertyName })->ToString() 

/// We get the property name using the "dinosaur strategy": 
/// good old macro concatenated with the empty string 
/// which in turn is formed in CheckFor() macro 
#define GetPropertyName(TheClassName, ThePropertyName) \ 
(gcnew System::String(#ThePropertyName)) + \ 
CheckForPropertyExistence(TheClassName, ThePropertyName)->Substring(0,0) 

/// To get properties from objects with no default constructor 
#define GetPropertyNameForObject(TheObject, ThePropertyName) \ 
(gcnew System::String(#ThePropertyName)) + \ 
(gcnew array<System::Object^> { (TheObject)-> ThePropertyName })->ToString()->Substring(0,0) 

/// Test for our macros 
int main(array<System::String ^> ^args) 
{ 
    /// Prints "Length" 
    /// We cannot use default constructor here 
    Console::WriteLine(GetPropertyNameForObject (gcnew System::String("test"), Length)); 

    /// Prints "Id" 
    Console::WriteLine(GetPropertyName (TheTimeZone, Id)); 

/// Uncomment and get the error 
    //Console::WriteLine(GetPropertyName (TheTimeZone, Id23)); 

    return 0; 
} 
+2

Abbastanza bello/hacky ... Non avresti potuto decorare CheckForPropertyExistence con un '1? NULL: yourcode', così in fase di esecuzione sono stati creati due oggetti in meno? – xanatos

+0

@xanatos: Sì, sembra essere una buona idea. Sono passati tre anni, non ricordo il ragionamento esatto per creare un oggetto. Forse solo la "soluzione trovata!" l'eccitazione mi ha impedito di fare come tu proponi. –

2

Le espressioni lambda in C# sono zucchero sintattico per i delegati, quindi sarà necessario trovare l'equivalente delegato dell'espressione lambda per poter avere la stessa funzionalità in C++/CLI.

per rendere la vita un po 'più facile, si potrebbe scoprire CLinq (vedere la sezione "Lambda espressioni"), che fornisce un C++/CLI wrapper per Linq

Nota: Non confondere il C++ 11 espressioni lambda con le espressioni lambda C#. Il primo è supportato solo per il codice nativo. È possibile utilizzare i lambda C++ 11, ma è necessario fare del lavoro extra per fornire loro delegati (this CodeProject article esplora l'argomento)

+0

Non c'è un "delegare equivalente", che è compatibile con il C++/CLI e runtime gestito. Tuttavia, ci sono macro trascurate! –

+0

@ViktorLatypov - Si prega di leggere sui delegati C++/CLI [qui] (http://www.drdobbs.com/cpp/184401980). – Attila

+0

Mi dispiace, Attila. Intendevo Lambdas con la sintassi "X => F (X)" usata nella domanda originale. System.Delegate è perfettamente valido, ma non esiste un modo semplice per decompilare il codice a cui si riferisce effettivamente. –

Problemi correlati