Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting Values on a derived class from the base class constructor using Reflection

I have two classes like this:

public abstract class MyBase
{
     protected MyBase(){
         Initialize();
     }

     protected IDictionary<string,string> _data;

     private void Initialize() {
         // Use Reflection to get all properties
         // of the derived class (e.g., call new MyDerived() then
         // I want to know the names "Hello" and "ID" here
         var data = GetDataFromBackend(propertyNamesFromDerived);
         _data = data;
     }
}

public class MyConcrete : MyBase
{
     public MyConcrete(){
         // Possibly use Reflection here
         Hello = _data["Hello"];
         ID = new Guid(data["ID"]);
     }

     public string Hello {get;set;}
     public Guid ID {get; set;}
}

As you see, I want the constructor of my base class to know about the properties of the derived class I'm instantiating.

Now, this seems like a huge and big code smell, so let me give some more background about my intentions, maybe there is a better way.

I have a backend system that stores Key/Value Pairs, essentially a Dictionary<string,string>. I want to abstract away working with this backend system in a way where people can create classes whose properties are Keys into the backend system. When they construct this object, it will automatically load the data from that system and initialize all the variables to it.

In other words, I've just reinvented serialization, except that I don't control the backend system and just rather make working with it really painless. I don't want callers to have to call Initialize() after constructing the object, because in 100% of the cases you have to initalize it after constructing.

I don't want to move the initialize code into the Derived Classes, except for string-to-business-object conversion.

Would I have to use a Factory? Or is it considered safe to look at the property names of a derived class in a base constructor? (Don't care about their values and that they aren't initialized, just need the names).

Or is there a better way altogether to provide a facade between a Dictionary of strings and a concrete business object?

Edit: This is .net 3.5, so no System.Dynamic which would make this trivial :(

Edit 2: After looking at the Answers and thinking through this some more, I guess my question really boils down to this now: Is calling GetType().GetProperties() from a base constructor in order to get the Names of Properties and if they are decorated with a certain Attribute safe?

like image 279
Michael Stum Avatar asked Aug 08 '26 14:08

Michael Stum


2 Answers

Wait, let's stop here for a second and do this properly. It shouldn't be MyBase's responsibility to do this.

So you write a class that manages getting stuff out of the backend for you, and you write a method on that class that is something like

T Get<T>() where T : new()

and you make Get responsible for reading the dictionary out of the backend and using reflection to populate an instance of T. Thus, you say

var concrete = foo.Get<MyConcrete>();

This isn't hard, and it's the right way to do it.

Incidentally, the code for Get is going to look something like

T t = new T();
var properties = typeof(T).GetProperties();
foreach(var property in properties) {
    property.SetValue(t, dictionary[property.Name], null);
}
return t;

where dictionary is your loaded up key/value pairs. It turns out there are more optimal ways to do this, but unless it's a bottleneck I wouldn't worry about it.

like image 84
jason Avatar answered Aug 10 '26 08:08

jason


The better way to do this would be to make the classes use the dictionary directly:

public string Hello {
    get { return (string)base.data["Hello"]; }
    set { base.data["Hello"] = value; }
}

You may want to call TryGetValue in the getter so that you can return a default value if the key isn't there. (You should probably do that in a separate method in the base class)

You can make a code snippet to make the properties easier to create.

If you don't want to do it this way, you can call GetType().GetProperties() to get PropertyInfo objects for the properties in your class, then call SetValue(this, value).
This will be slow; there are various tricks you can use to speed it up using expression trees, CreateDelegate, or IL generation.

like image 43
SLaks Avatar answered Aug 10 '26 08:08

SLaks