2012-05-09 9 views
6

widzę wiele wpisów dla robią to w C#, ale nie dla C++Jak można uzyskać nazwę właściwości jako ciąg w zarządzanym C++

Mam zestaw właściwości niektórych udało kodu C++, który jest używany przekazać dane między częścią C# a częścią C++. Po stronie C#, odpowiedź prezentowana here działa bardzo dobrze i chciałbym zrobić coś podobnego z C++. Kopia rozwiązania zawarte w 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)); 

Moim problemem jest to, że nie może wydawać się, aby uzyskać prawidłową składnię wywołania konkretnie tego paragrafu:

() => tz.DisplayName 

I nie może wydawać się znajdź zasób online, który wyjaśnia, jak zrobiłbym to w C++, więc jeśli ktokolwiek ma jakieś doświadczenie lub linki, byłbym wdzięczny za jakąkolwiek pomoc.

Odpowiedz

6

Walczyłem z tymi wszystkimi lambdami i fajnymi przestrzeniami nazw System :: Linq (łącznie z biblioteką CLinq i innymi rzeczami) na kilka godzin, a potem ... Pomyślałem: dlaczego nie zastosować decyzji czysto statycznej?

Po prostu zadeklarować

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

i wtedy możemy zrobić

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

dostać "ID" drukowane na ekranie.

Taa ... Teraz ta interesująca część. Bezpieczeństwo typu. Słyszałem burzę komentarzy dotyczącą tego rozwiązania ("NIE, to nie jest dobre, nie sprawdza, czy nazwaPropertyName jest w klasie!")

OK. Rozwiązanie: utwórzmy trochę bezsensownego kodu, używając makra, które używa nazwy ThePropertyName w fikcyjnej instancji 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) 

Teraz możemy dać kompletny próbki:

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

Całkiem piękne/hacky ... Nie możesz udekorować CheckForPropertyExistence z '1? NULL: yourcode', tak aby w czasie wykonywania były tworzone dwa obiekty mniej? – xanatos

+0

@xanatos: Tak, wydaje się to dobrym pomysłem. Minęły trzy lata, nie pamiętam dokładnego rozumowania, aby stworzyć obiekt. Może po prostu "znalezione rozwiązanie!" podniecenie przeszkodziło mi w robieniu tego, co proponujesz. –

2

Wyrażenia lambdy w języku C# są cukrem syntaktycznym dla delegatów, więc będziesz musiał znaleźć delegatowy odpowiednik wyrażenia lambda, aby móc korzystać z tej samej funkcjonalności w C++/CLI.

aby twoje życie trochę łatwiejsze, można zbadać CLinq (patrz rozdział „Lambda wyrażenia”), która zapewnia C++/CLI otoki dla Linq

Uwaga: Nie należy mylić C++ 11 wyrażenia lambda z wyrażeniami C# lambda. Ten pierwszy jest obsługiwany tylko dla kodu natywnego. Możliwe jest użycie lambdasa C++ 11, ale musisz wykonać dodatkową robotę, aby przekazać im delegatów (this CodeProject article bada temat)

+0

nie ma „delegować odpowiednik”, który jest kompatybilny z C++/CLI i udało wykonywania. Istnieją jednak pomijane makra! –

+0

@ViktorLatypov - Proszę przeczytać o delegatach C++/CLI [tutaj] (http://www.drdobbs.com/cpp/184401980). – Attila

+0

Przepraszam, Attila. Miałem na myśli Lambdę ze składnią "X => F (X)", która jest używana w pierwotnym pytaniu. System.Delegate jest całkowicie poprawny, ale nie ma łatwego sposobu dekompilacji kodu, do którego się odnosi. –

Powiązane problemy