74

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?

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
zanona
  • 12,345
  • 25
  • 86
  • 141
  • 3
    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. – Pierreten May 25 '10 at 13:51

11 Answers11

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");
Cirelli94
  • 1,714
  • 1
  • 15
  • 24
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 139
    Someone told me C# was far too strongly-typed. After a little reflection, I decided it wasn't all that bad. – deed02392 Sep 23 '13 at 13:18
  • 3
    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) – Garr Godfrey Aug 14 '15 at 20:16
45

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);
    }
}
Will Bickford
  • 5,381
  • 2
  • 30
  • 45
Richard Friend
  • 15,800
  • 1
  • 42
  • 60
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.

Stephan
  • 5,430
  • 2
  • 23
  • 31
7

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.

Ken Mc
  • 1,268
  • 15
  • 12
6

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

dynamic foo = account;
foo.Reference = "124ds4EE2s";
Rob Sedgwick
  • 4,342
  • 6
  • 50
  • 87
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);
    }
}
Stefan Michev
  • 4,795
  • 3
  • 35
  • 30
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;
        }
    }
}
Paul Williams
  • 16,585
  • 5
  • 47
  • 82
  • 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 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"]`. – Edward Aug 27 '21 at 15:54
1

You need to use Reflection:

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

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

Note that this will be very slow.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
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);
        }
Cliff Crerar
  • 433
  • 8
  • 24
0

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

rajesh
  • 1
  • 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. – ChriPf Nov 25 '21 at 12:18
  • If you have a new question, please ask it by clicking the [Ask Question](https://stackoverflow.com/questions/ask) button. Include a link to this question if it helps provide context. - [From Review](/review/late-answers/30431614) – Erik McKelvey Nov 26 '21 at 07:15
-1

Here is a simple example, I hope it helps

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();
}
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291