Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using AOP to provide default values for non-value-type parameters

0.00/5 (No votes)
11 Sep 2013 1  
How to apply default values to non-value-types at runtime using Aspect Oriented Programming.

Introduction

A while ago, Microsoft did consider allowing supplying default values for non-value types. Nothing came out of it except maybe Code Contracts, so if you need to allow for a null value for say a List<string> parameter, you have no choice, but to check for null and assign a default value yourself, or modify your logic flow to accommodate various scenarios. While this is a perfectly acceptable programming practice, sometime, you are just "to busy" (aka lazy) to implement it. This article will demonstrate how Aspect Oriented Programming, to be more specific, its PostSharp flavor, simplifies the task. This article will nto talk in detail about AOP or PostSharp. The reader is expected to do it in their own time.

Using the code

Let's start by showing the sample code:

[assembly: ConsoleApplication1.ApplyDefaults(AttributeTargetMembers = "regex:.*WithDefaults")]

namespace ConsoleApplication1
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Reflection;

    using PostSharp.Aspects;
    using PostSharp.Extensibility;

    internal class Program
    {
        public static DataSet GetsDataWithDefaults([Default] List<string> parameters = null)
        {
            try
            {
                var dsResult = new DataSet();
                //DoSomething here with the parameters
                return dsResult;
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        private static void Main(string[] args) { DataSet ds = GetsDataWithDefaults(); }
    }

    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Method)]
    public class ApplyDefaults : MethodInterceptionAspect
    {
        public override void OnInvoke(MethodInterceptionArgs args)
        {
            var parameterInfos = (from arg in args.Method.GetParameters() where arg.MayBeNull() && 
                 (args.Arguments[arg.Position] == null) select arg).Cast<ParameterInfo>();
            foreach (var info in parameterInfos)
            {
                Type t = info.ParameterType;
                args.Arguments[info.Position] = Activator.CreateInstance(t);
            }
            args.Proceed();
        }
    }

    [AttributeUsage(AttributeTargets.Parameter)]
    public class DefaultAttribute : Attribute
    {
    }

    public static class ReflectionExtensions
    {
        public static bool MayBeNull(this ParameterInfo arg) { 
          return (arg.NeedsDefault() && !arg.IsOut); }

        private static bool IsCustomAttributeDefined<T>(
          this ICustomAttributeProvider value) where T : Attribute { return value.IsDefined(typeof(T), false); }

        private static bool NeedsDefault(this ICustomAttributeProvider value) { 
          return value.IsCustomAttributeDefined<DefaultAttribute>(); }
    }
}

The only place in PostSharp where you can intercept and change parameter values is MethodInterceptionAspect. The implementation is fairly simple, by overriding the OnInvoke method, checking for a nullable parameter that is null and has been decorated with a [Default] attribute, than using Reflection (Activator) to create an instance of the class using discovered type and default public constructor. Because of this approach, it is recommended only to use it with parameter-less constructors. However, it is entirely possible to extend this method and provide an array of objects to use a constructor that best matches the specified number and type of parameters.

The aspect is than applied using multicast, to all methods, which name ends with WithDefaults. This is to improve performance by eliminating methods, which do not require this particular approach. However, you could apply the aspect to all methods in a class or assembly, as well as each method specifically, by decorating it with the ApplyDefaults attribute.

Points of Interest

AOP is a great time saver to eliminate lines of boiler plate code. Like any technology it has to be used with caution and on as-needed basis.

History 

  • Version 1.0: Posted on 9/11/2013.
  • Version 1.0.1 - Minor mod to code to allow all parameters to be processed, not just first.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here