Dependency Injection Attributes: On The Class or The Property
That title is really way too long. Anyway.
I did some refactoring in my Apollo code today involving the dependency injection for my entity system. Basically I have an Entity class which can hold a bunch of EntityComponents so I can build up aggregate objects. Part of this involves a system for dependency injection.
A basic scenario is that I have two components: a Position component and a Collision component. The Collision component requires a Position component to function, so in my system that component would have a property for the Position component and indicate that it requires that component to be on the entity and set to the property.
My first implementation had me placing attributes on the class level which were evaluated like so:
[RequiredComponent(typeof(Position), "Pos")]
public class Collision : EntityComponent
{
public Position Pos { get; set; }
}
So that makes sense to me. My class requires the partner component, so the attribute should be class level. The downside is that I have to explicitly specify both the type and the property name to set that object to. So tonight I decided that was too error prone and opted instead for placing the attributes on the properties like this:
public class Collision : EntityComponent
{
[InjectedComponent]
public Position Pos { get; set; }
}
As you can see it’s much cleaner, requires no arguments (though I do have an overloaded constructor that takes a boolean so you can flag it as an optional component), and gets the job done. However this isn’t quite as logical in my mind. Instead of saying “The Collision component requires the Position component” I’ve now just said “This property needs an injected component”.
I definitely prefer the new system as it’s cleaner and less error prone, but I’m still fighting with my head over which is actually the more logical placement. So what do you blogosphere residents think? Attribute on the class or attribute on the property?
Possibly Related Posts
(Automatically Generated)Simplifying Dependency Injection
XmlSerializer Misconceptions Part 1
IDictionary + XmlSerializer = Epic FAIL
Extension Methods and You
My Singleton Base Class

IMHO, cleaner is always better. However, that being said, I’ve never done anything with dependency injection so I haven’t any idea what the norm is.
Looking at the two versions of the class I’m having a hard time distinguishing whats going on. Having the RequiredComponent attribute gives me an idea whats expected although its ugly with the typeof and property name. The InjectedComponent attribute means nothing to me. It actually makes me ask more questions than it answers. If that attribute is normal for DI then all is cool. If I had a choice, and the end results were the same, then a “Required” attribute at the property would make me feel warm and happy.
Hey Nick,
I read your page here pretty religiously. I have noticed that you’ve used a lot of custom attributes in your projects. Someday, would you please do a really simply what, how, and why on putting together custom attributes. I’ve tried following MSDN and various other sites and I just don’t understand how they affect your code when you apply an attribute. If you ever get around to this, I will be eternally grateful. I understand that you are a busy guy however. You’ve got some great stuff on this site BTW!!!
I’d also prefer the new system. Especially because you got rid of the String – Strings are meant to contain data to make your code work. Additionally, refactoring mechanisms won’t work on Strings.
As a ground rule it’s better to have meta-data where it directly belongs. It’s obvious once you try to form natural sentences with metadata. “The dog (of my neighbor [who has a hairy dog]) has lice.” is inferior to “The [hairy] dog of my neighbor has lice.” in amout of “code” and readability.
Once you have more injections, you will also have a huge header of definitions on the class and then the property definitions – not cool.
A way of improving the system might be to make the attribute unnecessary – just provide an [OptionalComponent] and assume all other attributes (which return a component) as a RequiredComponent. But I don’t know what will be the more common case. If there’s none, keep it the way it is now.