Saturday, May 2, 2015

C# Building a Dictionary in Attributes

Introduction

The last blog I did about .NET attributes was to add a simple attribute to an existing method. This entry is about building a "Dictionary" of data into a set of attributes on a method. For example, I want the attributes to document each parameter of a method. So what I want is something that looks like this:


[Method(Description="This is the fibonacci sequence"
       ,Parameters={ {"in", "The input parameter" } })]
int Fibonacci(int in)
{
    if (in == 0 || in == 1) return 1;
    return Fibonacci(in - 2) + Fibonacci(in - 1);
}

Keep in mind this is NOT valid syntax in .NET (at least I don't think it is).

Attribute Definition

The above description won't work- only because I don't think that C# allows parameter definitions with complex objects as values. However, I found a solution that is pretty good at [1]. The partial solution is to separate each element that would be in the dictionary into a unique attribute. The attribute I put together looks like this:
                                                                                                        
[AttributeUsage(AttributeTargets.All,AllowMultiple=true)]
public class SomeParameter : Attribute
{
     private string m_name;
     private string m_description;

     public string Name 
     {
         get
         {
             return m_name;
         }
         set
         {
             m_name = value;
         }
     }


     public string Description
     {
         get
         {
             return m_description;
         }
         set
         {
             m_description = value;
         }
     }
}

I put one of the critical sections in bold. The trick is to set the AllowMultiple flag to true, which lets us actually add more than one of these attributes to the method [2]. To use this:

[SomeParameter(Name="input1",Description="The input to fib.")]
public int Fibonacci(int input1)
{
}

Note: Remember to put the public in front of the method! If it is not listed as public, it will not show up when using the reflection method I'm going to show below. This isn't a very good example since fibonacci only takes one parameter. However if I add a new method:


[SomeParameter(Name="parm1",Description="The first input to Calculate2.")]
[SomeParameter(Name="parm2",Description="The second input to Calculate2.")]
public int Calculate2(int parm1, int parm2)
{
}

Why do this? The reason I want to do this is to add a way to query more information about parameters and functions. Here is my complete program to illustrate how the multiple parameters can also be queried:

using System;


[AttributeUsage(AttributeTargets.All)]
public class SomeMethod : Attribute
{
    private string m_description;

    public string Description
    {

        get
        {
            return m_description;
        }

        set
        {
            m_description = value;
        }
    }
}


[AttributeUsage(AttributeTargets.All,AllowMultiple=true)]
public class SomeParameter : Attribute
{
    private string m_name;
    private string m_description;

    public string Name
    {
        get
        {
            return m_name;
        }

        set
        {
            m_name = value;
        }
    }


    public string Description
    {
        get
        {
            return m_description;
        }

        set
        {
            m_description = value;
        }
    }


}


public class HelloWorld
{
    HelloWorld()
    {
    }


    [SomeMethod(Description="Sample calculation")]
    [SomeParameter(Name="parm1", Description="Parameter #1")]
    [SomeParameter(Name="parm2", Description="Parameter #2")]
    public int Calculate2(int parm1, int parm2)

    {
        return parm1 + parm2;
    }


    static public void Main()
    {
        HelloWorld h = new HelloWorld();

        int output = h.Calculate2(4, 5);


        Type hwInfo = typeof(HelloWorld);

        System.Reflection.MemberInfo[] memberInfo = hwInfo.GetMembers();
        foreach(System.Reflection.MemberInfo mInfo in memberInfo)
        {
            Console.WriteLine(mInfo.ToString());

            object[] attributes = mInfo.GetCustomAttributes(true);
            Console.WriteLine("\tNumber of attributes: " + attributes.Length);
            for(int i=0; i<attributes.Length; i++)
            {
                System.Console.WriteLine("\t\t" + attributes[i]);
            }
        }
    }
}

This program goes through and prints out all of the methods available in the C# class.


REFERENCES

[1] - http://stackoverflow.com/questions/21265431/how-can-i-accept-a-dictionary-of-data-as-a-custom-net-attribute-parameter

[2] - https://msdn.microsoft.com/en-us/library/tw5zxet9.aspx