2012-05-09 10 views
6

Veo una gran cantidad de entradas por hacer esto en C#, pero nada para C++¿Cómo se puede obtener un nombre de propiedad como una cadena en C++ administrado

que tienen un conjunto de propiedades de algún código C++ que se utiliza lograron pasar los datos entre la porción C# y la porción C++. En el lado C#, la respuesta presentada here funciona muy bien y me gustaría hacer algo similar con C++. Copia de la solución contenida en Enlace:

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)); 

Mi problema es que me parece que no puede obtener la sintaxis correcta para la llamada, en concreto, en esta sección:

() => tz.DisplayName 

Me parece que no puede encuentre un recurso en línea que repase cómo haré esto en C++, así que si alguien tiene alguna experiencia o enlaces, realmente agradecería cualquier ayuda.

Respuesta

6

He luchado con todas esas lambdas y geniales espacios de nombres de System :: Linq (incluida la biblioteca CLinq y otras cosas) durante un par de horas y luego ... Solo pensé: ¿por qué no utilizar una decisión puramente estática?

simplemente declaramos el

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

y luego podemos hacer

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

para obtener el "Id" impreso en la pantalla.

Sí ... Ahora la parte interesante. El tipo de seguridad. Escucho el comentario de tormenta sobre esta solución ("¡NO! Esto no es bueno, no comprueba si ThePropertyName está en la clase")

OK. La solución: produzcamos algún código sin sentido utilizando una macro que use ThePropertyName en una instancia ficticia de 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) 

Ahora podemos dar a la muestra completa:

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

Bastante bonito/hacky ... ¿No podrías haber decorado el CheckForPropertyExistence con un '1? NULL: yourcode', de modo que en el tiempo de ejecución se crearon dos objetos menos? – xanatos

+0

@xanatos: Sí, parece ser una buena idea. Han pasado tres años, no recuerdo el razonamiento exacto para crear un objeto. ¡Quizás solo la "solución encontrada"! la emoción me impidió hacer lo que propones. –

2

Las expresiones Lambda en C# son azúcar sintáctico para los delegados, por lo que deberá encontrar el equivalente de delegado de la expresión lambda para poder tener la misma funcionalidad en C++/CLI.

para hacer su vida un poco más fácil, se podría explorar CLinq (véase la sección "Lambda expresiones") que proporciona una C++/CLI envoltura para LINQ

Nota: No se debe confundir el C++ 11 expresiones lambda con las expresiones lambda C#. El primero solo es compatible con el código nativo. Es posible utilizar C++ 11 lambdas, pero necesita hacer un trabajo extra para proporcionar delegados para ellos (this CodeProject article explora el tema)

+0

No hay "delegar equivalente", que es compatible con el C++/CLI y el tiempo de ejecución administrado. Sin embargo, hay macros que se pasan por alto. –

+0

@ViktorLatypov - Lea acerca de los delegados de C++/CLI [aquí] (http://www.drdobbs.com/cpp/184401980). – Attila

+0

Lo siento, Attila. Quise decir Lambdas con la sintaxis "X => F (X)" que se utilizan en la pregunta original. System.Delegate es perfectamente válido, pero no hay una manera fácil de descompilar el código al que realmente hace referencia. –

Cuestiones relacionadas