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;
}
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
@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. –