Sunday, March 8, 2015

C# .NET Attributes Data (Beginner's Guide)

Introduction

This guide is all about how to add metadata to a C# program. This entry is aimed at the beginning C# developer who has experience with other programming languages. Metadata has a variety of uses- at the company I work for we're going to use it to provide more verbose information to consumers of some given code. It's a very .NET-y solution to a problem we've already solved in C++ using parsers. The advantage to metadata is that it's a solution that is already supported by the .NET framework.

Please note that all of the code that I'm writing is compiled using Mono. Mono is a cross-platform tool that can compile C# code into an intermediate form which can then be run.

The Initial Program

For the initial program I decided to use the "Hello World" program provided by the Mono project [1]. This had the duel purpose of testing out my Mono installation. The original HW program was kind of simple for the type of meta-information I wanted to provide. So it was modified. Here is the original code (once again, credit to the mono project):

using System;

public class HelloWorld
{
    static public void Main()
    {
        Console.WriteLine("Hello Mono World!");
    }
}

Let's change the program to instantiate a HelloWorld object and use a method to print a message out.

using System;

public class HelloWorld
{
    public void PrintMsg(String msg)
    {
        Console.WriteLine(msg);
    }

    static public void Main()
    {
        HelloWorld hw = new HelloWorld();
        hw.PrintMsg("Hello World!");
    }
}

Adding an Attribute Class

The example class now has a basic method (PrintMsg). I want to start inserting special meta data to the class and some of its functions. Let's start with a simple description field. To achieve this a custom Attribute class must be written. I'm going to call the attribute class "SomeMethod":

using System;

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

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

This is a really simple attributes class that has one property called "Description". The Description property is implemented with the get/set methods. These are just shorthand ways to implement these basic accessors. It's also much more formal than most other languages.

The top line contains [AttributeUsage(AttributeTargets.All)] seems to help declare this class as an attribute that can be applied using metadata calls [2].

Applying the Custom Attribute

To apply the custom attribute, all that is necessary is to apply some metadata before the method and it automatically gets attached:

using System;

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

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


public class HelloWorld
{
    [SomeMethod(Description="This is a description of PrintMsg")]
    public void PrintMsg(String msg)
    {
        Console.WriteLine(msg);
    }

    static public void Main()
    {
        HelloWorld hw = new HelloWorld();
        hw.PrintMsg("Hello World!");
    }
}

I put the introduced attribute in bold.

Using Reflection to Query Attributes

The only step left is now querying the attributes placed on members/methods. This is pretty simple using a language feature called Reflection. So I modified the main part of the program to contain the following lines:

public class HelloWorld
{
    [SomeMethod(Description="This is a description of PrintMsg")]
    public void PrintMsg(String msg)
    {
        Console.WriteLine(msg);
    }

    static public void Main()
    {
        HelloWorld hw = new HelloWorld();
        hw.PrintMsg("Hello World!");

        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 is now modified to print out all of the methods inside of HelloWorld and whether or not there were custom attributes applied. The point of this step is to partially explore what can be queried with Reflection.

Learning Note: When I ran this program in Mono it was observed that my one simple class had more than one method. Being a veteran C++ developer I am familiar with methods (constructors, assignment operators, etc) being added to classes. It appears as though C# also does something similar adding: 

  • Boolean Equals(System.String)
  • Int32 GetHashCode()
  • System.Type GetType()
  • System.String ToString()
  • Void .ctor()
I must admit that these methods all look really useful! As a C# beginner I think another entry might be useful to describe what these methods do. 


If we want to detect when a method has an attribute, it's as simple as:

public class HelloWorld
{
    [SomeMethod(Description="This is a description of PrintMsg")]
    public void PrintMsg(String msg)
    {
        Console.WriteLine(msg);
    }

    static public void Main()
    {
        HelloWorld hw = new HelloWorld();
        hw.PrintMsg("Hello World!");

        Type hwInfo = typeof(HelloWorld);

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

            SomeMethod sm = 
               (SomeMethod) Attribute.GetCustomAttribute( mInfo
                                                        , typeof(SomeMethod));
            if (sm != null)
            {
                Console.WriteLine("\tHAS IT!\n");
            }
        }
    }
}

This program will now print out a message when the SomeMethod is obtained!

Conclusion

I really hate to admit this- C# looks awesome! I really enjoyed using Mono to run these examples. I know the examples were simple but I'm impressed with how the language defines its own custom attributes. In my next entry on this feature I'll explore how to add lists of things to attributes.


REFERENCES

[1] - http://www.mono-project.com/docs/getting-started/mono-basics/

[2] - https://msdn.microsoft.com/en-us/library/aa288454%28v=vs.71%29.aspx

[3] -https://msdn.microsoft.com/en-us/library/aa288454%28v=vs.71%29.aspx#vcwlkattributestutorialanchor3


No comments:

Post a Comment