Доступ к свойству объекта в виде строки и установка его значения

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

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

account.Reference;

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

account["PropertyName"];

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

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

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

DataBinder.Eval(account,"Reference") 

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

Есть идеи, как я могу это сделать?

11 ответов

Прежде всего, вы должны избегать использования этого; 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");

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

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);
    }
}

Я использовал метод отражения от Ричарда, но разработал метод 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() выдаст ошибку, если преобразование не работает.

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

dynamic foo = account;
foo.Reference = "124ds4EE2s";

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

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            
    }
}

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

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

С другой стороны, если вам нужно вести список пользовательских свойств, вы не можете использовать свойства 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;
        }
    }
}

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

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);
    }
}

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

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

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

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

Используйте тела отражения и выражения

              public dynamic this[string memberName]
        {
            get => GetType().GetProperty(memberName).GetValue(this, null);
            set => GetType().GetProperty(memberName).SetValue(this,value, null);
        }

как получить доступ к списку в объекте, используя отражение по имени строки
public List Table1 { get; установлен; } = новый список();

Вот простой пример, надеюсь, поможет

      static void Main(string[] args)
{
    Operators op = new Operators()
    {
        ID = 1,
        Name = "Edward",
        email = "e.lorenz@mail.com",
        Pass = "123456",
        Auth1 = "EDF3242523@FFSDGDF"
    };

    var typei = op.GetType();
    var ss = typei.GetProperties().Where(m => m.GetCustomAttributes<Password>().Count() > 0);

    foreach (var item in ss)
    {
        var text = typei.GetProperty(item.Name).GetValue(op).ToString();
        
        typei.GetProperty(item.Name).SetValue(op, Encrypt(text));
    }

    Console.WriteLine(op.Pass);
    Console.WriteLine(op.Auth1);

    Console.ReadKey();
}
Другие вопросы по тегам