How to create a variable where access its properties via string?

12

I would like to access by string the properties of an object: (EXAMPLE 1)

var cor = casa["cor"];
var tamanho = casa["tamanho"];

Instead of access like this: (EXAMPLE 2)

var cor = casa.cor;
var tamanho = casa.tamanho;

Creating this way I access through EXAMPLE 2:

var casa = new {
    cor = "verde",
    tamanho = 2000
}

How would I create the house object to gain access as in EXAMPLE 1?

    
asked by anonymous 13.10.2015 / 18:42

4 answers

7

The first pure and simple form can be obtained with a Dictionary

var casa = new Dictionary<string, object>();
casa.Add("cor", "verde");
casa.Add("tamanho", 2000);
var cor = casa["cor"];
var tamanho = casa["tamanho"];

See running dotNetFiddle with initialization syntax.

I would avoid doing this since the object is not typed. The first type of the structure indicates the type of the key. In the case string falls well. Because the value type can count several types, then you have to generalize and use object . That is, every entry in the dictionary will accept any value.

Another possible solution is to use ExpandoObject that gives the dynamism and can access with both syntax (not simultaneously):

dynamic obj = new ExpandoObject();
obj.cor = "verde";
obj.tamanho = 2000;
WriteLine(obj.cor);
WriteLine(obj.tamanho);
var obj2 = (IDictionary<string,object>)obj;
WriteLine(obj2["cor"]);
WriteLine(obj2["tamanho"]);

See working on dotNetFiddle .

Any solution to declare a normal class and try to access in this way is unnecessary. I might even be thinking, but I'd probably be using the wrong tool for the problem.

    
13.10.2015 / 18:50
6

You can use indexers as an example from Microsoft's own documentation ( link ):

p>
class Program
    {
        static void Main(string[] args)
        {
            List<Casa> lstCasas = new List<Casa>()
            {
                new Casa("Azul", 50),
                new Casa("Amarela", 70),
                new Casa("Branca", 90)
            };

            foreach (Casa _casa in lstCasas)
            {
                Console.WriteLine("Casa cor: {0}, tamanho: {1}", _casa["cor"], _casa["tamanho"]);
            }
            Console.Read();

            Casa _casaTemp = lstCasas[0];
            Console.WriteLine("Exception" + _casaTemp["Erro"]);
        }
    }

    public class Casa
    {
        private string cor;
        private int tamanho;

        public Casa(string _cor, int _tamanho)
        {
            this.cor = _cor;
            this.tamanho = _tamanho;
        }

        public object this[string strCampo]
        {
            get
            {
                switch (strCampo.ToLower())
                {
                    case "cor":
                        return this.cor;
                    case "tamanho":
                        return this.tamanho;
                    default:
                        throw new ArgumentException("Propriedade inválida");
                }
            }
        }
    }

A more general approach, as cited by @Cano Morrison Mendez would be using Generics. As shown below, one caveat to this approach is that you should use properties and not attributes, and the get methods of the properties should be public:

public class Casa
    {
        public string cor { get; private set; }
        public int tamanho { get; private set; }

        public Casa(string _cor, int _tamanho)
        {
            this.cor = _cor;
            this.tamanho = _tamanho;
        }

        public object this[string strCampo]
        {
            get
            {
                return GetValorPelaPropriedade(strCampo);
            }
        }

        private object GetValorPelaPropriedade(string strNomePropriedade)
        {

            PropertyInfo _propInfo = this.GetType().GetProperty(strNomePropriedade);

            if (_propInfo == null)
                throw new ArgumentException("Propriedade inválida");

            return _propInfo.GetValue(this, null);
        }
    }
    
13.10.2015 / 19:09
2

Just convert the object to dictionary. This answer has the method below that can serve well :

public static KeyValuePair<object, object> Cast<K, V>(this KeyValuePair<K, V> kvp)
{
    return new KeyValuePair<object, object>(kvp.Key, kvp.Value);
}

public static KeyValuePair<T, V> CastFrom<T, V>(Object obj)
{
    return (KeyValuePair<T, V>) obj;
}

public static KeyValuePair<object, object> CastFrom(Object obj)
{
    var type = obj.GetType();
    if (type.IsGenericType)
    {
        if (type == typeof (KeyValuePair<,>))
        {
            var key = type.GetProperty("Key");
            var value = type.GetProperty("Value");
            var keyObj = key.GetValue(obj, null);
            var valueObj = value.GetValue(obj, null);
            return new KeyValuePair<object, object>(keyObj, valueObj);
        }
    }

    throw new ArgumentException(" ### -> public static KeyValuePair<object, object> CastFrom(Object obj) : Erro : argumento obj deve ser do tipo KeyValuePair<,>");
}

Usage:

var dicionario = CastFrom(objeto);

Or this answer , which I opted for better as an extension of any object:

public static class ObjectToDictionaryExtension
{
    public static IDictionary<string, object> ToDictionary(this object source)
    {
        return source.ToDictionary<object>();
    }

    public static IDictionary<string, T> ToDictionary<T>(this object source)
    {
        if (source == null)
            ThrowExceptionWhenSourceArgumentIsNull();

        var dictionary = new Dictionary<string, T>();
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
            AddPropertyToDictionary<T>(property, source, dictionary);
        return dictionary;
    }

    private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
    {
        object value = property.GetValue(source);
        if (IsOfType<T>(value))
            dictionary.Add(property.Name, (T)value);
    }

    private static bool IsOfType<T>(object value)
    {
        return value is T;
    }

    private static void ThrowExceptionWhenSourceArgumentIsNull()
    {
        throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
    }
}

Usage:

var dicionario = objeto.ToDictionary();
    
13.10.2015 / 18:52
1

To access the properties or methods of an object you must use Reflection . You can even develop a hybrid class by encapsulating a key-value structure ( Dictionary ) to store the values by name and implement the Cor and Tamanho properties by assigning and retrieving values through keys, but the solution technically more adherent to Your question is using Reflection . Here is a working example that meets your need:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Casa casa = new Casa() { Cor = "Verde", Tamanho = 2000 };
            Wrapper wrapperCasa = new Wrapper(casa);

            wrapperCasa.SetPropetyValue<string>("Cor", "Vermelho");
            wrapperCasa.SetPropetyValue<int>("Tamanho", 2500);

            Console.WriteLine(wrapperCasa.GetPropetyValue<string>("Cor"));
            Console.WriteLine(wrapperCasa.GetPropetyValue<int>("Tamanho").ToString());
        }
    }

    class Casa
    {
        public string Cor { get; set; }
        public int Tamanho { get; set; }
    }

    class Wrapper
    {
        private object obj { get; set; }
        private Type tipo { get; set; }

        public Wrapper(object obj)
        {
            this.obj = obj;
            this.tipo = obj.GetType();
        }

        public void SetPropetyValue(string prop, object value)
        {
            this.SetPropetyValue<object>(prop, value);
        }

        public void SetPropetyValue<T>(string prop, T value)
        {
            this.GetPropertyInfo(prop).SetValue(this.obj, value, null);
        }

        public object GetPropetyValue(string prop)
        {
            return GetPropetyValue<object>(prop);
        }

        public T GetPropetyValue<T>(string prop)
        {
            return (T)this.GetPropertyInfo(prop).GetValue(this.obj, null);
        }

        private PropertyInfo GetPropertyInfo(string propriedade)
        {
            return this.tipo.GetProperty(propriedade);
        }
    }
}

I created an encapsulator ( Wrapper ) just to make it easier to use, because what actually happens is to use the main object ( Casa ) without typing, like object , and use Reflection with a object Type to invoke the desired methods and properties.

    
13.10.2015 / 19:37