Доступ к свойству объекта в виде строки и установка его значения - PullRequest
42 голосов
/ 25 мая 2010

У меня есть экземпляр класса Account. Каждый объект учетной записи имеет владельца, ссылку и т. Д.

Один из способов получить доступ к свойствам учетной записи - через средства доступа, подобные

account.Reference;

но я бы хотел иметь к нему доступ с помощью динамических селекторов строк, таких как:

account["PropertyName"];

как в JavaScript. Так что у меня будет account["Reference"], который будет возвращать значение, но я также хотел бы иметь возможность назначить новое значение после этого, например:

account["Reference"] = "124ds4EE2s";

Я заметил, что могу использовать

DataBinder.Eval(account,"Reference") 

чтобы получить свойство на основе строки, но с помощью этого я не могу присвоить значение свойству.

Есть идеи, как мне это сделать?

Ответы [ 8 ]

61 голосов
/ 25 мая 2010

Прежде всего, вы должны избегать использования этого; C # является языком со строгой типизацией, поэтому воспользуйтесь преимуществами безопасности типов и производительности, которые сопровождают этот аспект.

Если у вас есть веская причина для динамического получения и установки значения свойства (другими словами, когда тип и / или имя свойства не могут быть определены в коде), вам придется использовать отражение.

Самый встроенный способ выглядит так:

object value = typeof(YourType).GetProperty("PropertyName").GetValue(yourInstance);
...
typeof(YourType).GetProperty("PropertyName").SetValue(yourInstance, "value");

Однако вы можете кэшировать объект PropertyInfo, чтобы сделать его более читабельным:

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");

object value = prop.GetValue(yourInstance);
...
prop.SetValue(yourInstance, "value");
31 голосов
/ 25 мая 2010

Вы можете попробовать объединить индексатор с отражением ...

public object this[string propertyName]
{
    get
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        return property.GetValue(this, null);
    }
    set
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        property.SetValue(this,value, null);
    }
}
5 голосов
/ 12 января 2017

Я использовал метод отражения от Ричарда, но разработал метод set для обработки других используемых типов, таких как строки и нули.

public object this[string propertyName]
{
    get
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        return property.GetValue(this, null);
    }
    set
    {
        PropertyInfo property = GetType().GetProperty(propertyName);
        Type propType = property.PropertyType;
        if (value == null)
        {
            if (propType.IsValueType && Nullable.GetUnderlyingType(propType) == null)
            {
                throw new InvalidCastException();
            }
            else
            {
                property.SetValue(this, null, null);
            }
        }
        else if (value.GetType() == propType)
        {
            property.SetValue(this, value, null);
        }
        else
        {
            TypeConverter typeConverter = TypeDescriptor.GetConverter(propType);
            object propValue = typeConverter.ConvertFromString(value.ToString());
            property.SetValue(this, propValue, null);
        }
    }
}

Функция SetValue () выдаст ошибку, если преобразование не работает.

5 голосов
/ 04 сентября 2015

Если вы используете .Net 4, вы можете использовать динамическое ключевое слово сейчас.

dynamic foo = account;
foo.Reference = "124ds4EE2s";
5 голосов
/ 25 мая 2010

Если это ваши собственные объекты, вы можете предоставить индексатор для доступа к полям.Я не очень рекомендую это, но это позволило бы то, что вы хотите.

public object this[string propertyName]
{
    get
    {
        if(propertyName == "Reference")
            return this.Reference;
        else
            return null;
    }
    set
    {
        if(propertyName == "Reference")
            this.Reference = value;
        else
            // do error case here            
    }
}

Обратите внимание, что при этом вы потеряете безопасность типов.

2 голосов
/ 03 октября 2014

Лично я предпочитаю работать с методами расширения, поэтому вот мой код:

public static class ReflectionExtensions
{
    public static void SetPropertyValue(this object Target,
        string PropertyName,
        object NewValue)
    {
        if (Target == null) return; //or throw exception

        System.Reflection.PropertyInfo prop = Target.GetType().GetProperty(PropertyName);

        if (prop == null) return; //or throw exception

        object value = prop.GetValue(Target, null);

        prop.SetValue(Target, NewValue, null);
    }
}
1 голос
/ 25 мая 2010

Я согласен с предыдущими постерами, что вам, вероятно, нужно использовать свойства. Отражение очень медленное по сравнению с прямым доступом к свойству.

С другой стороны, если вам нужно вести список пользовательских свойств, вы не можете использовать свойства C #. Вам нужно притвориться, что вы Dictionary, или вам нужно выставить свойство, которое ведет себя как Dictionary. Вот пример того, как вы могли бы сделать так, чтобы класс Account поддерживал определяемые пользователем свойства:

public class Account
{
    Dictionary<string, object> properties;
    public object this[string propertyName]
    {
        get
        {
            if (properties.ContainsKey[propertyName])
                return properties[propertyName];
            else
                return null;
        }
        set
        {
            properties[propertyName] = value;
        }
    }
}
1 голос
/ 25 мая 2010

Вам нужно использовать Reflection:

PropertyInfo property = typeof(Account).GetProperty("Reference");

property.SetValue(myAccount, "...", null);

Обратите внимание, что это будет очень медленно.

...