Click here to Skip to main content
15,890,670 members
Articles / Programming Languages / C#
Tip/Trick

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

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
11 Sep 2013CPOL2 min read 7.1K   4  
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:

C#
[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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect BI Software, Inc.
United States United States
A seasoned IT Professional. Programming and data processing artist. Contributor to StackOverflow.

Comments and Discussions

 
-- There are no messages in this forum --