Accessing object property as string and setting its value
Asked Answered
S

11

76

I have an instance of the Account class. Each account object has an owner, reference, etc.

One way I can access an accounts properties is through accessors like

account.Reference;

but I would like to be able to access it using dynamic string selectors like:

account["PropertyName"];

just like in JavaScript. So I would have account["Reference"] which would return the value, but I also would like to be able to assign a new value after that like:

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

I've noticed I can use

DataBinder.Eval(account,"Reference") 

to get a property based on a string, but using this I can't assign a value to the property.

Any idea on how I could do that?

Sworn answered 25/5, 2010 at 13:44 Comment(1)
C# isn't a scripting language; usually when learning a new language; you'll need to learn the new idioms and conventions that go along with it.Chartier
C
104

First of all, you should avoid using this; C# is a strongly-typed language, so take advantage of the type safety and performance advantages that accompany that aspect.

If you have a legitimate reason to get and set the value of a property dynamically (in other words, when the type and/or property name is not able to be defined in the code), then you'll have to use reflection.

The most inline-looking way would be this:

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

However, you can cache the PropertyInfo object to make it more readable:

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

object value = prop.GetValue(yourInstance);
...
prop.SetValue(yourInstance, "value");
Cogen answered 25/5, 2010 at 13:47 Comment(2)
Someone told me C# was far too strongly-typed. After a little reflection, I decided it wasn't all that bad.Peppy
you can save a lot of coding (and less code=less bugs) using this for persistence. Use it like ruby and make your db table match your properties and focus on algorithms, not endless typing (both strong and keyboard)Footstone
N
47

You could try combining the indexer with reflection...

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);
    }
}
Nought answered 25/5, 2010 at 15:20 Comment(1)
+1 Works for me as well. Note that you need to handle property possibly being null.Catchup
C
8

I used the reflection method from Richard, but elaborated the set method to handle other types being used such as strings and nulls.

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

The SetValue() function will throw an error if the conversion doesn't work.

Culosio answered 12/1, 2017 at 5:36 Comment(0)
C
7

If they are your own objects you could provide an indexer to access the fields. I don't really recommend this but it would allow what you want.

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

Note that you lose type safety when doing this.

Curium answered 25/5, 2010 at 13:56 Comment(0)
S
6

If you are using .Net 4 you can use the dynamic keyword now.

dynamic foo = account;
foo.Reference = "124ds4EE2s";
Sybilsybila answered 4/9, 2015 at 10:29 Comment(1)
This does not answer the original questionChalet
R
3

I agree with the previous posters that you probably do need to be using the properties. Reflection is very slow compared to direct property access.

On the other hand, if you need to maintain a list of user-defined properties, then you can't use C# properties. You need to pretend you are a Dictionary, or you need to expose a property that behaves like a Dictionary. Here is an example of how you could make the Account class support user-defined properties:

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;
        }
    }
}
Rawalpindi answered 25/5, 2010 at 14:3 Comment(1)
This is exactly what I need. In my case, there are several numerical properties that I need to access by name. So I use Dictionary<string, float> properties; to serve as backing fields. Furthermore, each property is actually defined. So a property can be either accessed via Account.PropertyX or Account["PropertyX"].Plast
G
3

I personally prefer to work with extension methods so here is my code :

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);
    }
}
Gnomic answered 3/10, 2014 at 13:40 Comment(0)
B
1

You need to use Reflection:

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

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

Note that this will be very slow.

Beaufert answered 25/5, 2010 at 13:48 Comment(0)
L
1

Use reflection and expression bodies

        public dynamic this[string memberName]
        {
            get => GetType().GetProperty(memberName).GetValue(this, null);
            set => GetType().GetProperty(memberName).SetValue(this,value, null);
        }
Lack answered 20/4, 2021 at 23:50 Comment(0)
X
0

how to access the list in an object using reflection by string name
public List Table1 { get; set; } = new List();

Xylograph answered 25/11, 2021 at 11:49 Comment(2)
Answers are not the place to ask new questions... - please create a new question. Also you probably should give a bit more context or more complete code to enable others to find an answer to your question.Encumbrance
If you have a new question, please ask it by clicking the Ask Question button. Include a link to this question if it helps provide context. - From ReviewLoyce
P
-1

Here is a simple example, I hope it helps

static void Main(string[] args)
{
    Operators op = new Operators()
    {
        ID = 1,
        Name = "Edward",
        email = "[email protected]",
        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();
}
Pentobarbital answered 3/6, 2021 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.