0

I have an object called CottonCandy. CottonCandy has a few properties:

  • Mass
  • Volume
  • TotalSugar
  • Colors

What would be great is if I could add something to CottonCandy so that I could retrieve properties like so:

var colors = cottonCandy["Colors"];

From reading this it looks like you can get the value with:

cottonCandy.GetType().GetProperty("Colors").GetValue(cottonCandy, null);

and you can easily wrap that in a method:

var colors = cottonCandy.GetPropertyValue("Colors");

but I'd really prefer the key/value syntax cottonCandy["Colors"]. Is there any way to make that happen?

user875234
  • 2,399
  • 7
  • 31
  • 49
  • So you want to set a properties value based on the properties name? Than why not just write some wrapper-method around that reflection-code, which by the way is the way to go. Alternativly just use a `Dictionary`. – MakePeaceGreatAgain Aug 30 '18 at 13:10
  • What exactly is Colors? – Reinstate Monica Cellio Aug 30 '18 at 13:14
  • 1
    Is your use case that you want to have some class but have an unknown set of properties you might want to "attach" to it which you'll only know at runtime? If so, you may be looking for [`ExpandoObject`](https://learn.microsoft.com/en-us/dotnet/api/system.dynamic.expandoobject?view=netframework-4.7.2) but to use it from C#, you have to use `dynamic` as well. (But if you're going down this route, ask yourself whether a statically typed language like C# is the right tool for the job) – Damien_The_Unbeliever Aug 30 '18 at 13:14
  • Yeah, that's what I'm saying with `GetPropertyValue` but idk my initial thought is getting a properties value like that is a new concept in the code that I'd rather not introduce. It's not a pretty situation, I admit that. – user875234 Aug 30 '18 at 13:15
  • 2
    You could combine the reflection approach with [Indexers](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/using-indexers). The general problem with your approach here is how you'd make this work given that each property might be of a different type. – Kirk Larkin Aug 30 '18 at 13:15
  • Colors is an array of `System.Drawing.Color`. but it's just an example. – user875234 Aug 30 '18 at 13:15
  • 1
    Can you explain why you want to do anything like this, rather than not simply access the property as per normal, with `cottonCandy.Colors`? – Reinstate Monica Cellio Aug 30 '18 at 13:16
  • @Archer because that's the situation I have found myself in and a refactor would only marginally improve the overall quality of the code so I'm doing the best I can man, lol. – user875234 Aug 30 '18 at 13:18
  • What if your class has a method? How to access this? – MakePeaceGreatAgain Aug 30 '18 at 13:19
  • 2
    I'd consider it vastly improving the quality of the code, if it becomes standard that every other developer will recognise instantly, but either way it sounds like an XY Problem. The fact that you think you *have* to implement this sounds more like the problem to me. – Reinstate Monica Cellio Aug 30 '18 at 13:19
  • 1
    You can probably do it with [indexers](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/) – Oram Aug 30 '18 at 13:22
  • @Archer the main problem is I am trying to keep the code general so I don't have to rewrite portions of the logic for different widgets. The method I'm working on needs to take in a property name as a string and then return the value of that property on two different objects. Now maybe I could redesign it at a higher level but I think I will always run into the same problem. It's working now, `cottonCandy["Colors"]` but looking at it idk. No matter how you do it it will seem non-standard to someone seeing it for the first time. ¯\_(ツ)_/¯ – user875234 Aug 30 '18 at 13:56
  • It's easy for me to stand on the side line and tell you how it should be done ;) – Reinstate Monica Cellio Aug 30 '18 at 13:57

5 Answers5

6

There is a way actually. You can use indexers. But you'll need to have same field types or to box values in object type.

class CottonCandy
{
    private int Mass { get; set; }
    private int Volume { get; set; }
    private int TotalSugar { get; set; }
    private int Colors { get; set; }

    public int this[string field]
    {
        get
        {
            PropertyInfo propertyInfo = typeof(CottonCandy).GetProperty(field);
            if(propertyInfo == null)
                throw new ArgumentException("Invalid field");
            return (int)propertyInfo.GetValue(this);
        }
    }
}
rattrapper
  • 431
  • 2
  • 5
  • 17
2

You can do this

public class CottonCandy
    {

        public string Mass { get; set; }
        public string Volume { get; set; }
        public string TotalSugar { get; set; }
        public string Colors { get; set; }
        public string this[string key]
        {
            get
            {
                switch (key)
                {
                    case "Mass":
                        return this.Mass;
                    case "Volume":
                        return this.Volume;
                    case "TotalSugar":
                        return this.TotalSugar;
                    case "Colors":
                        return this.Colors;
                    default:
                        return "";
                }

            }
            set
            {
                switch (key)
                {
                    case "Mass":
                        this.Mass = value; break;
                    case "Volume":
                        this.Volume = value; break;
                    case "TotalSugar":
                        this.TotalSugar = value; break;
                    case "Colors":
                        this.Colors = value; break;
                    default:
                        break;
                }
            }
        }


    }
1

This is not key/vaue syntax. However, if you decide use extensions, you can use this extension methods. There are similar extensions in this Nuget package

public static T GetFieldValue<T>(this object obj, string fieldName)
{
    T result;
    PropertyInfo propertyInfo = obj.GetType().GetProperty(fieldName);
    if (propertyInfo != null)
    {
        result = (T)propertyInfo.GetValue(obj);
    }
    else
    {
        throw new FieldAccessException($"{fieldName} cannot be found");
    }
    return result;
}

You can reach more extensions via this link. Maybe unit tests give clue about methods usage and results.

UPDATE:

You can combine extension method and indexer like this:

public class TestClass
{
    public string this[string field]
    {
        get => this.GetFieldValue<string>(field);
        set => this.TrySetFieldValue(field, value);
    }
}
Adem Catamak
  • 1,987
  • 2
  • 17
  • 25
1

Try having a look at object indexers: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/

A way of achieving this would be with an indexer and making use of the "nameof" operator to compare the indexer string value with the available properties. This would make the code a bit more verbose but avoids the need for reflection.

However, make sure this is indeed what you really want because you no longer get the benefits of compile time checking if you access properties in this fashion.

Emil Roman
  • 66
  • 4
0

If your class is a simple data-objet without any behaviour and only such simple properties, you can make it a Dictionary instead:

var map = new Dictionary<string, whatever> { { "Colors", ... }, { "Mass", ... } ... };
map["Colors"] = ...;

Otherwise you should use reflection as you´ve already mentioned.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111