After describing it so much, I felt the need to go ahead and implement it. So here goes...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
namespace DefaultValue
{
public static class DefaultValueExtensions
{
private static Dictionary<Type, Delegate> _dctTypeInitializers = new Dictionary<Type, Delegate>();
public static void InitializePropertyDefaults<T>(this T item)
{
Type type;
Delegate d;
Action<T> action;
type = typeof(T);
lock (DefaultValueExtensions._dctTypeInitializers)
{
if (!DefaultValueExtensions._dctTypeInitializers.TryGetValue(type, out d))
DefaultValueExtensions._dctTypeInitializers[type] = d = DefaultValueExtensions.CreateTypeInitializer(type);
}
action = d as Action<T>;
action(item);
}
private static Delegate CreateTypeInitializer(Type type)
{
List<Expression> lstExpressionBody;
ParameterExpression itemParam;
lstExpressionBody = new List<Expression>();
itemParam = Expression.Parameter(type, "item");
foreach (var tuple in DefaultValueExtensions.GetPropertiesAndValues(type))
{
lstExpressionBody.Add
(
Expression.Assign
(
Expression.Property(itemParam, tuple.Item1),
Expression.Constant(tuple.Item2)
)
);
}
lstExpressionBody.Add(Expression.Empty());
return Expression.Lambda(Expression.Block(lstExpressionBody), itemParam).Compile();
}
private static IEnumerable<Tuple<PropertyInfo, object>> GetPropertiesAndValues(Type type)
{
object[] defaultValueAttributes;
DefaultValueAttribute defaultValueAttribute;
foreach (PropertyInfo prop in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
defaultValueAttributes = prop.GetCustomAttributes(typeof(DefaultValueAttribute), true);
if ((defaultValueAttributes != null) && (defaultValueAttributes.Length > 0))
{
defaultValueAttribute = defaultValueAttributes[0] as DefaultValueAttribute;
yield return new Tuple<PropertyInfo, object>(prop, defaultValueAttribute.Value);
}
}
}
}
}
With its usage as:
public MyClass()
{
this.InitializePropertyDefaults();
}