Lately I played around a bit with Aspect Oriented Programming, especially with PostSharp. I wanted to see how I could use it to reduce the amount of infrastructural code that clutters a common class like this:
public class Person : INotifyPropertyChanged
{
private string firstName;
private string lastName;
private int age;
public string LastName
{
get { return this.lastName; }
set
{
// check the argument value
if (value == null)
{
throw new ArgumentNullException("value");
}
if (value == "")
{
throw new ArgumentException("Last Name must not be empty.", "value");
}
if (value.Length > 50)
{
throw new ArgumentException("Last Name must not be longer than 50 characters.",
"value");
}
// do nothing when trying to set the same value twice
if (value == this.lastName)
{
return;
}
// set the value
this.lastName = value;
// fire PropertyChanged events
this.OnPropertyChanged("LastName");
this.OnPropertyChanged("FullName");
}
}
public string FirstName
{
get { return this.firstName; }
set
{
// check the argument value
if (value != null)
{
if (value.Length > 30)
{
throw new ArgumentException("First Name must not be longer than 30 characters.",
"value");
}
}
// do nothing when trying to set the same value twice
if (value == this.firstName)
{
return;
}
// set the value
this.firstName = value;
// fire PropertyChanged events
this.OnPropertyChanged("FirstName");
this.OnPropertyChanged("FullName");
}
}
public string FullName
{
get
{
return (this.firstName + " " + this.lastName).Trim();
}
}
public int Age
{
get { return this.age; }
set
{
if (value < 18 || value > 65)
{
throw new ArgumentOutOfRangeException("value", "Age must be between 18 and 65.");
}
this.age = value;
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion // INotifyPropertyChanged implementation
} // class Person
Most of the code of this class is value-checking and a bit of INotifyPropertyChanged-logic, like you frequently have it with business objects and some sort of Model-View-Presenter/Controller-style UI coupling or WPF. But no matter how important such code is in terms of correct program behavior, it is repetitive and boring - certainly not the kind of stuff that made me become a developer. Code like this is mostly written in the 'classic' copy-paste-modify coding style, with all the problems that this may bring...
Now look at that:
[Validate, NotifyPropertyChanged(OnPropertyChanged = "OnPropertyChanged")]
public class PersonWithAspects : INotifyPropertyChanged
{
[NotNullOrEmpty, MaximumLength(50)]
[PropertyChangedDependency("FullName")]
public string LastName { get; set; }
[PropertyChangedDependency("FullName")]
[MaximumLength(30)]
public string FirstName { get; set; }
[InRange(18, 65)]
[NoPropertyChangedEvent]
public int Age { get; set; }
public string FullName
{
get
{
return (this.FirstName + " " + this.LastName).Trim();
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion // INotifyPropertyChanged implementation
} // class PersonWithAspects
This version of the Person class is absolutely identical to the first one in terms of behavior (except that it throws a different exception type), but it has almost no hand-written code, only some declarative attributes. This reduced the number of code lines for the properties in this simple example from 36 to 11 - or by about 70%! Wow...
I did it with the ValidationAspects aspect library, that sits on top of the PostSharp aspect weaver and allows for placing Design by Contract - like attributes on properties (and also on method arguments). Additionally, I took the INotifyPropertyChanged sample from the PostSharp 1.5 download and modified it slightly to include the NoPropertyChangedEvent and PropertyChangedDependency attributes. It was not much work, and the result is quite impressive, I think.
Besides the pure technical aspects (much better readability and expressiveness, dramatically reduced error-proneness etc.), code like this gives me the warm feeling of having done a good job. In fact, deleting code (while preserving functionality) is one of my favorite activities in writing software. I know that this might sound a bit strange from someone who calls himself a software developer - but if you think about it for a minute, it perfectly makes sense...