Accessing object property as string and setting its value

C#StringEvalAccessorDatabinder

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

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

Solution 3 - C#

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.

Solution 4 - C#

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.

Solution 5 - C#

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

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

Solution 6 - C#

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

Solution 7 - C#

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

Solution 8 - C#

You need to use Reflection:

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

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

Note that this will be very slow.

Solution 9 - C#

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

Solution 10 - C#

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

Solution 11 - C#

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionzanonaView Question on Stackoverflow
Solution 1 - C#Adam RobinsonView Answer on Stackoverflow
Solution 2 - C#Richard FriendView Answer on Stackoverflow
Solution 3 - C#StephanView Answer on Stackoverflow
Solution 4 - C#Ken McView Answer on Stackoverflow
Solution 5 - C#Rob SedgwickView Answer on Stackoverflow
Solution 6 - C#Paul WilliamsView Answer on Stackoverflow
Solution 7 - C#Stefan MichevView Answer on Stackoverflow
Solution 8 - C#SLaksView Answer on Stackoverflow
Solution 9 - C#Cliff CrerarView Answer on Stackoverflow
Solution 10 - C#rajeshView Answer on Stackoverflow
Solution 11 - C#edward lorenzView Answer on Stackoverflow