Modificar valores de las propiedades de una clase

Estoy más que seguro que el post actual es una de aquellas cosas que casi todo el mundo conocía, pero a mí me ha dado una especie de subidón el descubrirlo. Explico el problema.

En un programa que estoy haciendo me he encontrado en la situación de que tengo que importar unos datos que vienen de una serie de proveedores. Hasta aquí ha sido un poco aburrido puesto que unos lo envían en formato .txt, otros en Access y, por supuesto, cada uno con su formato particular. Ya digo, aburrido, pero hasta el momento ha ido saliendo. En la implementación, lo que he hecho ha sido crearme una clase más o menos estándar con los datos que necesito y, sea cual sea el proveedor, relleno una lista con esa clase para poder hacer un tratamiento homogéneo.

  • Jesús, ¿para qué cuentas esta película?, ¿no puedes ir al grano?.
  • Ya sabes que me gusta ponerme en situación siempre
  • Si todo te va tan bien, ¿dónde está el problema entonces?

Pues el problema está en un proveedor concreto, que tiene una serie de variables que, aunque discretas, son indeterminadas en número. Intento explicarlo con un ejemplo que se asemeja mucho a la situación que me he encontrado.

Ejemplo
En nuestra aplicación tenemos, en la base de datos, las partidas de gastos que necesita nuestra empresa. A grandes rasgos, sería algo así como :

 

   1: public class Caja
   2: {
   3:     public double Personal { get; set; }
   4:     public double Maquinaria { get; set; }
   5:     public double GastosVarios { get; set; }
   6: }

Ahora nos envían un fichero en el que hay una serie de códigos los cuales, por supuesto, nada tienen que ver con nuestras partidas, así que se nos ocurre hacer una tabla en la base de datos para que sea el propio usuario el que cree una relación entre los códigos del proveedor y los de nuestra clase. En el código siguiente, para no andar a vueltas con la base de datos, he incluido un par de colecciones que permiten simular la lectura de dichos datos.

   1: class Aplicacion
   2:   {
   3:       public static void Main()
   4:       {
   5:           // Simulamos los datos que podríamos extraer de 
   6:           // una tabla de asignación
   7:           // Formato: <keyProveedor,keyDb>
   8:           Dictionary<string, string> tabla =
   9:              new Dictionary<string, string>();
  10:           tabla.Add("SUELDO", "Personal");
  11:           tabla.Add("CENA_NAVIDAD", "GastosVarios");
  12:           tabla.Add("PC", "Maquinaria");
  13:           tabla.Add("IMPRESORA", "Maquinaria");
  14:           tabla.Add("PROPINA_INFORMATICO", "GastosVarios");
  15:  
  16:           // Simulamos también un fichero leído del proveedor
  17:           // Formato: <keyProveedor,importe>
  18:           KeyValuePair<string, double>[] datos = new KeyValuePair<string,double>[5];
  19:           datos[0] = new KeyValuePair<string, double>("PC", 1200);
  20:           datos[1] = new KeyValuePair<string, double>("IMPRESORA", 600);
  21:           datos[2] = new KeyValuePair<string, double>("SUELDO", 1200);
  22:           datos[3] = new KeyValuePair<string, double>("SUELDO", 1500);
  23:           datos[4] = new KeyValuePair<string, double>("PROPINA_INFORMATICO", 4000);
  24:  
  25:           
  26:           Caja caja = new Caja();
  27:           
  28:           // Bucle por los valores importados 
  29:           foreach (KeyValuePair<string,double> kpv in datos)
  30:           {
  31:               // Se obtiene el equivalente proveedor->interno
  32:               string keyDb = tabla[kpv.Key];
  33:               // Se añade el valor a la clase Caja
  34:               caja.AddGasto(keyDb, kpv.Value);
  35:           }
  36:           Console.WriteLine(caja.ToString());
  37:           Console.ReadKey();
  38:       }
  39:   }

En el ejemplo es la colección tabla la que el usuario habría modificado de tal manera que relacionase los códigos internos nuestros con los del proveedor que nos suministra los datos. El resto, como se puede apreciar, es tan sólo un bucle por los datos leídos y la llamada a un módulo de la clase Caja que deberemos crear.

Ahora, en la clase Caja, tan sólo nos queda crear un par de módulos. Uno que permita leer el valor de una propiedad (getValue) y el otro, lógicamente, que permita escribir un valor en dicha propiedad (AddGasto).

   1: public class Caja
   2: {
   3:     #region Partidas presupuestarias
   4:     public double Personal { get; set; }
   5:     public double Maquinaria { get; set; }
   6:     public double GastosVarios { get; set; }
   7:     #endregion
   8:  
   9:  
  10:     /// <summary>
  11:     /// Añade el precio a la propiedad pasada como parámetro
  12:     /// </summary>
  13:     /// <param name="propiedad">Nombre de la propiedad de la clase</param>
  14:     /// <param name="importe">Valor que se quiere añadir</param>
  15:     public void AddGasto(string propiedad, double importe)
  16:     {
  17:         // Recupera el valor actual del importe
  18:         double importeActual = getValue(propiedad);
  19:  
  20:         // Establece el valor a la propiedad
  21:         PropertyInfo p = this.GetType().GetProperty(propiedad);
  22:         p.SetValue(this, importe + importeActual, null);
  23:     }
  24:  
  25:     /// <summary>
  26:     /// Obtiene el valor de la propiedad 
  27:     /// </summary>
  28:     /// <param name="propiedad">Nombre de la propiedad</param>
  29:     double getValue(string propiedad)
  30:     {
  31:         PropertyInfo p = this.GetType().GetProperty(propiedad);
  32:         return (double)p.GetValue(this, null);
  33:     }
  34:  
  35:     /// <summary>
  36:     /// Obtiene un array con las propiedades de la clase 
  37:     /// </summary>
  38:     string[] GetPropiedades()
  39:     {
  40:         return this.GetType().GetProperties().Select(p => p.Name).ToArray();
  41:     }
  42:  
  43:     public override string ToString()
  44:     {
  45:         StringBuilder sb = new StringBuilder();
  46:         foreach (string p in GetPropiedades())
  47:         {
  48:             sb.AppendFormat(" - {0},{1,10:N}n", p.PadRight(30, '.'), getValue(p));
  49:         }
  50:         return sb.ToString();
  51:     }
  52: }

Tal como comentaba al principio, es más que probable que sea el único que no había hecho hasta ahora uso del namespace System.Reflection, pero como nunca había tenido la necesidad de hacer un tipo de asistente de este estilo, ni me había preocupado.

2 thoughts on “Modificar valores de las propiedades de una clase

  1. En mi opinión estas cometiendo varios errores, el principal es que mezclas lógica de negocio con tu entidad de almacenamiento de datos, debes separarlos y de esta forma tu programa sera mucho mas sencillo, en este caso no tiene ningún sentido que utilices reflexión, un saludo.

  2. Pues con esto vas a flipar:

    *** Obtener el nombre de una propiedad en tiempo de ejecución ***

    -> Te creas un método “GetPropertyName” y… MAGIA:

    using System.Linq.Expressions;

    public static string GetPropertyName(Expression> propertyExpression)
    {
    return (propertyExpression.Body as MemberExpression).Member.Name;
    }

    // …
    Coin c = new Coin();
    string testPrice = GetPropertyName(() => c.Price);

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *