2010-11-19 20 views
17

En C#/.NET se puede hacer algo como esto:Propiedades similares a C# en C++ nativo?

someThing.text = "blah"; 
String blah = someThing.text; 

Sin embargo, el código anterior no realmente interactúan con la cadena de texto del algo directamente, se utiliza un get y propiedad establecida. Del mismo modo, se pueden usar propiedades de solo lectura.

¿Hay alguna manera de hacer algo similar en native C++? (no C++ .NET)

+0

No soy un experto, pero por qué no dejar que 'someThing.text' ser un miembro del público si se va a tratar como uno de todos modos ? – flies

+2

Exponer miembros (incluso como de solo lectura) a través de getter y setters es malo OO. Estás exponiendo la representación interna de tu objeto al mundo. Incluso si esto está ligeramente protegido mediante el uso de métodos (ocultos detrás del azúcar sintáctico de las propiedades), proporciona una API pública que debe mantenerse. La pregunta es por qué estás tratando de exponer a tus miembros? El objeto debe usar la representación interna para realizar tareas que no lo expongan para que otras personas realicen tareas. En lugar de exponer la implementación, exponer un método de acción que utiliza la representación. –

+0

Cuestiono la utilidad de pagar un costo de eficiencia para oscurecer el código. –

Respuesta

15

En .NET las propiedades son azúcar sintáctico para las funciones reales get y set que se emiten detrás de escena (de hecho, son más que azúcar sintáctica porque las propiedades se emiten en la IL resultante y podrían ser usado con Reflection). Entonces en C++ necesitarás escribir explícitamente esas funciones ya que no existe tal noción como propiedad.

+0

Pero, ¿cómo sabe si el el usuario quiere obtener o configurar? ¿Es necesario utilizar la sobrecarga del operador? – jmasterx

+1

Cuando está en el lado izquierdo del operador de asignación ('=') que está configurando, cuando está a la derecha está consiguiendo y el compilador es lo suficientemente inteligente como para resolver esto. –

+0

Escribir dos funciones en eso devolverá una referencia, y una que devolverá una referencia constante es muy útil, el compilador averiguará cuándo llamar a cuál. Pero esto permitirá hacer estas llamadas en cualquier lugar (dentro de las funciones const). – Jbad26

0

No, no lo hay. Usted acaba de crear funciones get y set:

someThing.setText("blah"); 
std::string blah = someThing.getText(); 
2

Una propiedad en .NET se asocia con un get y/o una función set miembro, por lo que es realmente el azúcar solo sintáctica. Lo más cerca que se puede obtener con C++ es el uso de la sobrecarga para dar el getter y setter el mismo nombre:

const std::string &test() const { return text_; } 
void test(const std::string &value) { text_ = value; } 

Obviamente, todavía se tiene que proporcionar paréntesis para la llamada:

someThing.text("blah"); 
String blah = someThing.text(); 
15

Advierto usted: no es C++ nativo; es específico de Microsoft solo. Pero se puede usar declspec(property):

struct S { 
    int i; 
    void putprop(int j) { 
     i = j; 
    } 

    int getprop() { 
     return i; 
    } 

    __declspec(property(get = getprop, put = putprop)) int the_prop; 
}; 

int main() { 
    S s; 
    s.the_prop = 5; // THERE YOU GO 
    return s.the_prop; 
} 

cf MSDN, declspec(property).

+0

Aún así, ¡esto es genial para aquellos que hacen proyectos de plataforma X con MSVC! buen hallazgo :) – jmasterx

+0

Whoa, nunca he oído hablar de esto ... –

37

ADVERTENCIA: ¡Esta es una respuesta irónica y es terrible!

Sí, es una especie de posible :)

template<typename T> 
class Property 
{ 
private: 
    T& _value; 

public: 
    Property(T& value) : _value(value) 
    { 
    } // eo ctor 

    Property<T>& operator = (const T& val) 
    { 
     _value = val; 
     return *this; 
    }; // eo operator = 

    operator const T&() const 
    { 
     return _value; 
    }; // eo operator() 
}; 

Luego de declarar su clase, declarando propiedades para sus miembros:

class Test 
{ 
private: 
    std::string _label; 
    int   _width; 

public: 
    Test() : Label(_label) 
      , Width(_width) 
    { 
    }; 

    Property<std::string> Label; 
    Property<int>   Width; 
}; 

Y llama a C# de estilo!

Test a; 
a.Label = "blah"; 
a.Width = 5; 

std::string label = a.Label; 
int width = a.Width; 
+9

He intentado esto en el pasado, aunque ahora tiendo a evitarlo porque las personas que leen el código no se dan cuenta de que no es solo un campo público, ya que no es común en C++ ... Sin embargo, me sorprendió descubrir que el compilador por lo general es lo suficientemente bueno como para hacer que esto sea tan eficiente como la asignación directa al campo. – jcoder

+0

Sí, de ahí tuve mi "terrible" descargo de responsabilidad. Lo pensé un poco más y estoy seguro de que se puede mejorar para que se puedan proporcionar funciones personalizadas set/get para sets/get no triviales. –

+0

+1 para un uso creativo de plantillas. – paercebal

2

Sí, pero es específico del vendedor. Microsoft tiene declspec (propiedad). La implementación de C++ Builder es un poco más avanzada (a través de la palabra clave __property específica del proveedor) ya que puede tener accesadores indexados (que pueden ser de cualquier tipo que desee).

también mira esto (sin depender de proveedores palabras clave específicas): Respuesta http://www.codeproject.com/KB/cpp/cpp_property_indexer.aspx

1
#include <iostream> 
#include <string> 

using namespace std; 

// ------------------------------------------------------------------ 

#define PROPERTY_GET_SET(CLASS, NAME, TYPE) GetSetProperty<CLASS, TYPE> NAME() { return GetSetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME, &CLASS::set_##NAME); } 
#define PROPERTY_GET(CLASS, NAME, TYPE)  GetProperty<CLASS, TYPE> NAME() { return GetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME); } 
#define PROPERTY_SET(CLASS, NAME, TYPE)  SetProperty<CLASS, TYPE> NAME() { return SetProperty<CLASS, TYPE>(this, &CLASS::set_##NAME); } 

template <typename CLASS, typename TYPE> 
struct GetSetProperty { 
    typedef TYPE (CLASS::*Getter_t)() const; 
    typedef void (CLASS::*Setter_t)(TYPE); 
    GetSetProperty(CLASS* instance, Getter_t getter, Setter_t setter) : m_instance(instance), m_getter(getter), m_setter(setter) {} 
    operator TYPE() const { return (this->m_instance->*this->m_getter)(); } 
    GetSetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; } 
    CLASS* const m_instance; 
    const Getter_t m_getter; 
    const Setter_t m_setter; 
}; 

template <typename CLASS, typename TYPE> 
struct GetProperty { 
    typedef TYPE (CLASS::*Getter_t)() const; 
    GetProperty(CLASS* instance, Getter_t getter) : m_instance(instance), m_getter(getter) {} 
    operator TYPE() const { return (this->m_instance->*this->m_getter)(); } 
    CLASS* const m_instance; 
    const Getter_t m_getter; 
}; 

template <typename CLASS, typename TYPE> 
struct SetProperty { 
    typedef void (CLASS::*Setter_t)(TYPE); 
    SetProperty(CLASS* instance, Setter_t setter) : m_instance(instance), m_setter(setter) {} 
    SetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; } 
    CLASS* const m_instance; 
    const Setter_t m_setter; 
}; 

template <typename CLASS, typename TYPE> 
ostream& operator<<(ostream& ostr, const GetSetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; } 

template <typename CLASS, typename TYPE> 
ostream& operator<<(ostream& ostr, const GetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; } 

// ------------------------------------------------------------------ 

class Dummy 
{ 
public: 

    Dummy() : m_value1(42) {} 

    PROPERTY_GET_SET(Dummy, Value1, int); 
    PROPERTY_GET_SET(Dummy, Value2, const string&); 

protected: 

    virtual int   get_Value1() const { return this->m_value1; } 
    virtual void   set_Value1(int value) { this->m_value1 = value; } 

    virtual const string& get_Value2() const { return this->m_value2; } 
    virtual void   set_Value2(const string& value) { this->m_value2 = value; } 

private: 

    int m_value1; 
    string m_value2; 
}; 


int main(int argc, char* argv[]) { 

    Dummy d; 

    cout << d.Value1() << endl; 
    d.Value1() = 3; 
    cout << d.Value1() << endl; 

    cout << d.Value2() << endl; 
    d.Value2() = "test"; 
    cout << d.Value2() << endl; 

    return 0; 
} 

// ------------------------------------------------------------------ 
4

Moo-Jugo se ve muy bien, pero tiene un inconveniente: no es posible utilizar estas propiedades como expresiones normales de tipo T, como puedes en C#.

Por ejemplo,

  • a.text.c_str() no se compilará (‘class Property<std::basic_string<char> >’ has no member named ‘c_str’)
  • std::cout << a.text no se compilará ya sea (template argument deduction/substitution failed)

que sugeriría la siguiente mejora a template<typename T> class Property:

T& operator()() 
{ 
    return _value; 
} 
T const& operator()() const 
{ 
    return _value; 
} 

A continuación, puede acceder a los miembros de la propiedad con (), tales como:

char const *p = a.text().c_str(); 

Y puede utilizar la propiedad en expresiones donde debe deducirse del tipo:

std::cout << a.text(); 
0

Por qué no usar el lenguaje C# en lugar de C++ para el desarrollo nativo? Para eso, puede usar la utilidad IL2BC para generar código nativo a partir de la fuente C# y/o el código de bytes MSIL?

IL2BC se puede encontrar en este sitio

http://csnative.codeplex.com

Cuestiones relacionadas