0

I have the following class example here:

public class Foo
{
    public int Id { get; set; }

    public string Bar { get; set; }
    public string FooBar { get; set; }
    public string Fizz { get; set; }
    public string Buzz { get; set; }

    public static Foo Create(int id, string property, string value)
    {
        return new Foo
        {
            Id = id,

        };
    }
}

Now, however, i want to set for example only Bar to a value in the Create method of the class if the propertyname is Bar . So i took a look at C# setting/getting the class properties by string name but i can't get this to work in the create method. Same goes for Setting a property by reflection with a string value so i'm kinda lost here.

As a small bit of clarification. I can get the above methods to work in the create, however not in the return which is where it needs to work.

The solution below seems to work for us for now, however, i think it could be optimized.

public class Foo
{
    public int Id { get; set; }

    public string Bar { get; set; }
    public string FooBar { get; set; }
    public string Fizz { get; set; }
    public string Buzz { get; set; }

    public static Foo Create(int id, string property, string value)
    {
        return new Foo
        {
            WebshopCustomerId = webshopCustomerId,
            Bar = (typeof(Foo)).GetProperty(property).Name == "Bar" ? value : null,
            FooBar = (typeof(Foo)).GetProperty(property).Name == "FooBar" ? value : null,
            Fizz = (typeof(Foo)).GetProperty(property).Name == "Fizz" ? value : null,
            Buzz = (typeof(Foo)).GetProperty(property).Name == "Buzz" ? value : null,
        };
    }
}
Vincentw
  • 164
  • 3
  • 17
  • If you cant get it to work show us what you did.. we might be able to help you fix it – BugFinder Oct 08 '18 at 08:17
  • I'd advise against solving this using reflection. Instead put those properties into a Dictionary which will free you from having to recompile every time you add a new property. – Filburt Oct 08 '18 at 08:18
  • @Filburt The properties are pretty static, and when they change we need to change them in other places as well. So that would not be an problem. Also, the reason we place this in a class is for serialization later. – Vincentw Oct 08 '18 at 08:20
  • 1
    Why do you need that? Which problem do you try to solve? – Yurii N. Oct 08 '18 at 08:26
  • It still looks suspiciously like a anti-pattern to seemingly have a class depend on the property being set in it's constructor. I guess your actual class doesn't only have 4 properties but considerably more. Otherwise you could simply have 4 overloading constructors. – Filburt Oct 08 '18 at 08:27
  • @YuriyN. This class gets passed to a Queue as a message after which it gets picked up by a function for later processing. This converts it to a JSON message and it gets posted to an API. However, when a customer changes a setting, only 1 of the 4 values gets changed (and all 4 are required) – Vincentw Oct 08 '18 at 08:33
  • Serializing a Dictionary to Properties looks rather simply using [Json.NET](https://www.newtonsoft.com/json/help/html/SerializeDictionary.htm) so you shouldn't have to worry about that. – Filburt Oct 08 '18 at 09:01
  • @Filburt. While i do agree on that, we currently have an azure function already running and this is an extension to that. I have however found an working solution so i will add that to the post. I am however open to improvements – Vincentw Oct 08 '18 at 09:08
  • Why have a `Create` method at all? The user could just call `var instance = new Foo {Bar = value};` and not even need your method to begin with. – Chris Dunaway Oct 08 '18 at 18:31

3 Answers3

2

You can avoid reflection and keep everything simpler using a dictionary of Actions:

public class Foo
{
    public int Id { get; set; }

    public string Bar { get; set; }
    public string FooBar { get; set; }
    public string Fizz { get; set; }
    public string Buzz { get; set; }

    private static Dictionary<string, Action<Foo, string>> _propertySetters =
        new Dictionary<string, Action<Foo, string>>()
        {
            { "Bar", (foo, value) => foo.Bar = value },
            { "FooBar", (foo, value) => foo.FooBar = value },
            { "Fizz", (foo, value) => foo.Fizz = value },
            { "Buzz", (foo, value) => foo.Buzz = value },
        };

    public static Foo CreateWithProperty(int id, string property, string value)
    {
        if (String.IsNullOrEmpty(property) || !_propertySetters.ContainsKey(property))
            throw new ArgumentException("property");

        var instance = new Foo { Id = id };

        var setter = _propertySetters[property];

        setter(instance, value);

        return instance;
    }
}

With this approach, you can even change the property names, while keeping the configuration values the same. Depending upon the situation, it could be a huge advantage.

Having said this, I feel that your requirement could probably be better answered with more context information and a slightly different design. YMMV.

Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53
1
public static Foo Create(int id, string property, string value)
{
    Foo ret = new Foo
    {
        Id = id
    };
    foreach (FieldInfo element in typeof(Foo).GetFields())
        if (element.Name == property)
            element.SetValue(ret, value);
    foreach (PropertyInfo element in typeof(Foo).GetProperties())
        if (element.Name == property)
            element.SetValue(ret, value);
    return ret;
}

Something looking like this should work for you, you could also use

ret.GetType().GetProperty(property).SetValue(ret, value);
ret.GetType().GetField(property).SetValue(ret, value);

But you'll have to handle errors.

nalka
  • 1,894
  • 11
  • 26
0

It will work for you, but you still need to check if property is a valid property.

       public static Foo Create(int id, string property, string value)
       {
           Foo foo = new Foo() { Id = id };

           PropertyInfo propertyInfo = foo.GetType().GetProperty(property);
           propertyInfo.SetValue(foo, Convert.ChangeType(value, propertyInfo.PropertyType), null);
           return foo;
       }
koviroli
  • 1,422
  • 1
  • 15
  • 27
  • While this would work in the create, it would not work in the return function which is where it needs to work. In the create i was able to implement the 2 examples which i linked in my original post. However i am unable to figure out how to set only Bar in the return. – Vincentw Oct 08 '18 at 08:23
  • So if I understand well, you'd like to do everything inside your return new block? – koviroli Oct 08 '18 at 08:30
  • Yes, i have added an new example i am currently working on to see if that works for me. – Vincentw Oct 08 '18 at 08:31