
You probably have written more than one piece of code below. And what is more likely - dozens of times. This is usually written when you need to use some kind of repository that will provide data for your application. If you have little time and you are in a hurry, this is a great way to get something in such a way that it will be loaded into memory only when you need it, but not earlier (for example, the save operation entailed a call to the serialization subsystem, however, before that, it was not needed). And in fact, it turns out that on the one hand this piece of code is the same, and on the other, it has to be written more than once.
As a rule, I and many programmers prefer to use an IoC container in this place to solve problems of this kind. However, it is not always so easy to do, especially when I program in the absence of Dependency Injection in the library I use (WinForms, WebForms, ...). Let's
see why solving this problem without using
PostSharp , you will spend much more time and do more work.

So, how often did you come across such code:
private IProductRepository _productRepository;
private IProductRepository ProductRepository
{
get
{
if (_productRepository == null )
{
_productRepository = new ProductRepository();
}
return _productRepository;
}
}
* This source code was highlighted with Source Code Highlighter .
While the property is of type IPropertyRepository, the getter of this property is still tightly bound to the ProductRepository type. If you need to change “new ProductRepository ()” to “new RevisedProductRepository ()” or to “new ProductRepository (2011)”, you will need to go through all places of the template code and update it. Of course, the hands immediately reach for Find-> Replace, Copy-> Paste ... However, you all know this situation, right? Firstly, you will spend a lot of time on replacements, and secondly, almost every member of your team will have to do this (when you have to make regular replacements). And, since everyone is already actively using IoC containers, you might think that this example is exaggerated. But believe me, such examples are very common! Anyway, using the IoC container, we get the following code:
')
private IProductRepository _productRepository;
private IProductRepository ProductRepository
{
get
{
if (_productRepository == null )
{
_productRepository = ObjectFactory.GetInstance<IProductRepository>();
}
return _productRepository;
}
}
* This source code was highlighted with Source Code Highlighter .
Now, as you can see, ObjectFactory takes care of creating new instances. If you need to create new instances of the class, add parameters to the designer and perform other operations during the creation of your object, you can do it in one place without affecting other parts of the program (I’ll just note that “ObjectFactory” is a static class in
StructureMap . However, you can use absolutely any IoC container which one you like best). Now our code looks much better, and the development team doesn't have to worry much about implementation: only about the interface. This is the real inversion of dependence, since Now the class depends only on the interface, not on the implementation.
The code has become much cleaner, but still contains “null” checks and lazy initialization that will be found throughout our application (if we use lazy loading quite often, of course). Also, it’s not so easy for us to change the same IoC container, if it stopped arranging us. And if at some point you decided that checking for null is not enough (for example, your program has become multitask), you will again have to crawl all over the code and change, change, change ... Let's see how you can finally solve this problem using
PostSharp :
[LoadDependency] private IProductRepository _productRepository;
[ Serializable ]
public sealed class LoadDependencyAttribute : LocationInterceptionAspect
{
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue(); // fetches the field and populates the args.Value
if (args.Value == null )
{
var locationType = args.Location.LocationType;
var instantiation = ObjectFactory.GetInstance(locationType);
if (instantiation != null )
{
args.SetNewValue(instantiation);
}
args.ProceedGetValue();
}
}
}
* This source code was highlighted with Source Code Highlighter .
Now we have a declarative approach to using dependencies. You just mark any field with the attribute “LoadDependency”, and it will be initialized by the IoC container during the first call (see the first line). Thus, you protected yourself from constantly writing template code for each property that would use lazy-loading, null checks, thread-safety, and other other changes that you would have to do, write each property separately. Now all your actions will be in the same class.
Using a very simple and small (only 19 lines) aspect, we compressed the resulting code from 12 lines to one. We removed a lot of senselessly repeating code (
DRY ) and replaced hard dependencies with clean interfaces (
DIP ). We brought the lazy-loading to our own class, and the resolution of dependencies to our own (
SRP ). Reducing, thus, the number of dependencies to a minimum.
This very simple example actually helps a lot in my daily development and makes it much easier.
Now, let's assume that all dependencies are known at the compilation stage. Also assume that the dependencies will not change while the application is running. In this case, we no longer need the Service Locator. We can override the CompileTimeInitialize method from LocationInterceptionAspect to resolve dependencies at compile time and save the type to some field. Then, while the application is running, you can use Activator.CreateInstance to create the desired object.
[ Serializable ]
public sealed class LoadDependencyAttribute : LocationInterceptionAspect
{
private Type _type;
public override bool CompileTimeValidate(PostSharp.Reflection.LocationInfo locationInfo)
{
_type = DependencyMap.GetConcreteType(locationInfo.LocationType);
if (_type == null )
{
Message.Write(SeverityType.Error, "002" ,
"A concrete type was not found for {0}.{1}" ,
locationInfo.DeclaringType, locationInfo.Name);
return false ;
}
return true ;
}
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (args.Value == null )
{
form.LogListBox.Items.Add( "Instantiating UserService" );
args.SetNewValue(Activator.CreateInstance(_type));
args.ProceedGetValue();
}
}
}
* This source code was highlighted with Source Code Highlighter .
If you have not configured any type, then you will get an error at the compilation stage, and not at the application operation stage, which is very convenient. I hope that you are already beginning to understand that PostSharp is a very powerful tool. Of course, I am not interested in using this method everywhere, where it is necessary or not necessary. However, I am sure that it is useful in many circumstances.
Another way to improve the resulting aspect is to make a check at compile time that the aspect is correctly used. For example, it makes no sense to use the LoadDependency aspect for applied to a field if this field is not an interface. Because it would mean that our dependencies are hard-coded and it is necessary to change everything again! :) So let's add a couple of extra checks:
public override bool CompileTimeValidate(PostSharp.Reflection.LocationInfo locationInfo)
{
if (!locationInfo.LocationType.IsInterface)
{
Message.Write(SeverityType.Error, "001" ,
"LoadDependency can only be used on Interfaces in {0}.{1}" ,
locationInfo.DeclaringType, locationInfo.Name);
return false ;
}
_type = DependencyMap.GetConcreteType(locationInfo.LocationType);
if (_type == null )
{
Message.Write(SeverityType.Error, "002" ,
"A concrete type was not found for {0}.{1}" ,
locationInfo.DeclaringType, locationInfo.Name);
return false ;
}
return true ;
}
* This source code was highlighted with Source Code Highlighter .
To get compilation errors, set the “LoadDependency” attribute for a field that is of any type, but not an interface:

So, with a simple aspect coupled with an IoC container, we got rid of hard dependencies and sample code and made our application easy to maintain, easy to test and read the source code. And now, no less important, our application fully follows the principles of SOLID. Now you save a lot of time, not only at the stages of development and testing, but also at the support stage.
Other translations and links: