访问对象属性并将其值设置为字符串

74

我有一个 Account 类的实例。每个账户对象都有一个所有者、参考等。

我可以通过访问器之类的方式来访问账户属性,例如:

account.Reference;

但是我希望能够使用动态字符串选择器来访问它,例如:

account["PropertyName"];

就像在 JavaScript 中一样。因此我将有 account["Reference"],它将返回该值,但我也希望能够像下面这样分配一个新值:
account["Reference"] = "124ds4EE2s";

我发现我可以使用


DataBinder.Eval(account,"Reference") 

如何根据字符串获取属性,但使用此方法无法为属性赋值。

对于这个问题有什么想法吗?


3
C#不是一种脚本语言;通常在学习一门新语言时,你需要学习相应的惯用语和约定。 - Pierreten
11个回答

104
首先,你应该避免使用这个;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");

141
有人告诉我 C# 类型太强了。经过一番思考,我认为并没有那么糟糕。 - deed02392
3
使用这种持久化方法可以大量减少编码工作(更少的代码=更少的错误)。像 Ruby 一样使用它,让数据库表与属性匹配,并专注于算法,而不是无休止的打字(无论是强制还是键盘)。 - Garr Godfrey

45
你可以尝试将索引器和反射结合起来使用...
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);
    }
}

4
+1 对我也适用。请注意需要处理属性可能为空的情况。 - Will Bickford

7
如果这些是你自己的对象,你可以提供一个索引器来访问这些字段。我不太建议这样做,但它可以实现你想要的功能。
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            
    }
}

请注意,这样做会失去类型安全性。

7

我使用了Richard的反射方法,但是我详细说明了set方法以处理其他类型的使用,例如字符串和null值。

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()函数将抛出错误。


6

如果你在使用.Net 4,现在可以使用dynamic关键字了。

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

1
这并没有回答原始的问题。 - userSteve

3

我同意之前的回答者,你可能需要使用属性。与直接访问属性相比,反射速度非常慢。

另一方面,如果你需要维护一个用户定义属性列表,则无法使用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
这正是我所需要的。在我的情况下,有几个数值属性需要通过名称访问。因此,我使用 Dictionary<string, float> properties; 作为后备字段。此外,每个属性实际上都被定义了。因此,可以通过 Account.PropertyXAccount["PropertyX"] 访问属性。 - Edward

3

我个人更喜欢使用扩展方法,因此这是我的代码:

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

你需要使用反射:

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

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

请注意,这将非常缓慢。

1
使用反射和表达式体。
        public dynamic this[string memberName]
        {
            get => GetType().GetProperty(memberName).GetValue(this, null);
            set => GetType().GetProperty(memberName).SetValue(this,value, null);
        }

0
如何使用反射通过字符串名称访问对象中的列表
public List Table1 { get; set; } = new List();

回答不是提出新问题的地方...请创建一个新问题。此外,您可能应该提供更多上下文或更完整的代码,以便他人能够找到您问题的答案。 - ChriPf
如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - Erik McKelvey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接