Introduction

The dissertation investigates attributes. You’ll see how you can define attributes on various items within your program. We shall also discuss the most innovative features the .NET framework has to offer: custom attributes, a mechanism that allows you to associate custom metadata with program elements. This metadata is created at compile time and implanted in an assembly. You can then scrutinize the metadata at runtime using reflection. We also come to understanding the use of reflection in custom attributes.

Attributes

As illustrated in earlier articles, the .NET compiler generated metadata descriptions for all defined and reference types. However, the developer can integrate additional metadata into an assembly using attributes. So attributes are like adjectives, which are used for metadata annotation similar to COM IDL that can be applied to a given type, assembly, modules, methods etc, The .NET framework stipulates two types of attributes implementations Predefined Attributes and Custom Attributes.

Attributes are types derived from the System.Attribute class. This is an abstract class defining the required services of any attribute. Here is the syntax of an attribute as following;

[type: attributeName(parameter1, parameter2,………n)]

The attribute name is the class name of the attribute. Attributes can have zero or more parameters. The following code sample shows an attributes implementation in which we are declaring a method as deprecated using the obsolete attribute:

using System;

namespace attributes
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Attributes sample");
            TestMethod();
            Console.ReadKey();
        }
        [Obsolete("Deprecated Method",false)]
        public static void TestMethod()
        {
            Console.WriteLine("Hello world");
        }
    }
}

The following figure shows the MSIL code of the TestMethod method as displayed in ILDASM. Notice the custom directive that defines the Obsolete attribute.

Role of Attributes

Attributes might be useful for documentation purpose. They fulfill many roles, including describing serialization, indicating conditional compilation, specifying import linkage, and setting class blueprint. Attributes allows information to be defined and applied to almost any metadata table entry. This extensible metadata information can be queried at run time dynamically alter the way code executes.

The C# compiler itself has been programmed to discover the presence of numerous attributes during the compilation process. For example, if the csc.exe compiler discovers an item being annotated with the [obsolete] attribute, it will display a compiler warning in the IDE error list.

Predefined Attributes

The predefined attributes have been defined by Microsoft as a part of .NET FCL, and many of them receive special support from the C# compiler. This implies that, for those particular attributes, the compiler could customize the compilation process in a specific way.

The System.Attribute base class library provides a number of attributes in various namespaces. The following table gives a snapshot of some predefined attributes.

Attributes Description
[Serialization] By marking this attributes, a class is able to persist its current state into stream.
[NonSerialization] It specifies that a given class or file should not persist during the serialization process.
[Obsolete] It is used to mark a member or type as deprecated. If they are attempted to be used somewhere else, the compiler issues a warning message.
[DllImport] This allows .NET code to make a call to an unmanaged C or C++ library.
[WebMethod] This is used to build XML web services and the marked method is being invoked by HTTP request.
[CLSCompliant] Enforce the annotated items to conform to the semantics of CLS.

To illustrate the predefined attributes in action, let's create a console-based application to apply the implementation of them

[Serialization] and [NonSerialization]

Here, assume that you have built a test class that can persist in a binary format using the [Serialization] attribute.

[Serializable]
public class test
 {
 public test() { }

 string name;
 string country;
 [NonSerialized]
 int salary;
 }

Once the class has been compiled, you can view the extra metadata by using the ildasm.exe utility. You can notice the red triangle where these attributes are recorded using the serializable token and the salary field is tokenized using a nonserilaized attribute as follows:

[WebMethod]

The following example depicts the implementation of XML web services. Here, the UtilityWebService class is annotated with [WebService] attributes. This class defines two methods that are marked with [WebMethod] attributes.

Want to learn more?? The InfoSec Institute Reverse Engineering course teaches you everything from reverse engineering malware to discovering vulnerabilities in binaries. These skills are required in order to properly secure an organization from today's ever evolving threats. In this 5 day hands-on course, you will gain the necessary binary analysis skills to discover the true nature of any Windows binary. You will learn how to recognize the high level language constructs (such as branching statements, looping functions and network socket code) critical to performing a thorough and professional reverse engineering analysis of a binary. Some features of this course include:

  • CREA Certification
  • 5 days of Intensive Hands-On Labs
  • Hostile Code & Malware analysis, including: Worms, Viruses, Trojans, Rootkits and Bots
  • Binary obfuscation schemes, used by: Hackers, Trojan writers and copy protection algorithms
  • Learn the methodologies, tools, and manual reversing techniques used real world situations in our reversing lab.
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class UtilityWebService : System.Web.Services.WebService {

    public UtilityWebService () {

        //Uncomment the following line if using designed components
        //InitializeComponent();
    }

    [WebMethod]
    public string HelloWorld() {
        return "Hello World";
    }

    [WebMethod]
    public int addition(int a,int b)
    {
        return a+b;
    }

}

[DLLImport]

The following code plays around the unmanaged assembly user32.dll to utilize its existing method using the [DLLImport] attributes:

using System;
using System.Runtime.InteropServices;
namespace attributes
{
    public class test
    {
        [DllImport("user32.dll", EntryPoint = "MessageBox")]
        public static extern int ShowMessageBox(int hWnd,string text, string caption,uint type);
    }
    class Program
    {
        static void Main(string[] args)
        {
            string caption = "Hello World";
            string text = "Sample Article on DLLImport Attribute";
            test.ShowMessageBox(0, text, caption, 0);
            Console.ReadKey();
        }

    }
}

Note: a member can be assigned more than one attribute and can be applied multiple times itself.

Once this code is compiled successfully, it produces the following output:

[CLSCompliant]

If you annotate this attribute at assembly or module level and you try to place the following non-CLR-compliant code, the compiler issues a warning message.

Custom Attributes

We can create custom attributes for private usage or to be published in a library for others. The following steps form the definitive procedure for creating custom attributes:

  1. The custom attribute class should be derived from System.Attribute
  2. The Attribute name should accomplish with the Attribute suffix.
  3. Set the probable targets with the AttributeUsage attribute.
  4. Implement the class constructor and write-accessible properties.

The first step in building a custom attribute is to create a new class FunWith with an Attribute suffix that deriving from the System.Attribute class. Then define the class constructor and write accessible property as Company subsequently.

[AttributeUsage(AttributeTargets.Class)]
public class FunwithAttribute : Attribute
 {
 public FunwithAttribute(string s)
 {
 this.Company = s;
 }
 public string Company { get; set; }
 }

Now, it is time to apply custom attribute class on another class. So we are creating another class test that has the FunWith attribute annotation, in which we are passing the company name information.

[Funwith("HCL Technology")]
public class test
    {
        public test(string name, string country)
        {
            this.EmpName = name;
            this.Country = country;
        }
        public string FullDetails()
        {
            string str = EmpName + "-" + Country;
            return str;
        }

        private string EmpName;
        private string Country;
    }
    class Program
    {
        static void Main(string[] args)
        {
            test obj = new test("Ajay","India");
            Console.WriteLine("Result:{0}",obj.FullDetails());
            Console.ReadKey();
        }
    }

After compiling this program, it yields the following output:

Output:
Result: Ajay – India

Well, as you can see, the custom attribute does not have any impact on the final output. But we can see the annotation happen by attributes at the metadata level by using ildasm.exe as follows:

Custom defined attributes are sometimes valuable simply as information. However, the real power lies in associating with an attribute. You can read custom attributes with reflection using Attribute.GetCustomAttribute and Type.

The following code illustrates accessing the custom attribute values at runtime using reflection. Here, we are storing the custom attributes members in an array of object type, then looping through it to get the property value:

static void Main(string[] args)
        {
            MemberInfo info = typeof(test);
            object[] attrib = info.GetCustomAttributes(typeof(FunwithAttribute), false);
            foreach (Object attribute in attrib)
            {
                FunwithAttribute a = (FunwithAttribute)attribute;
                Console.WriteLine("Company: {0}", a.Company);

            }
       }

For more understanding, the following code illustrate the actual utilization of custom attributes values. Here, we are displaying some string value based on the custom attribute Boolean value. The output varies on status True or False values.

using System;
using System.Reflection;

    public class CheckStatus : Attribute
    {
        private bool Val = false;
        public bool status
        {
            get { return Val; }
        }
        public CheckStatus(bool val)
        {
           Val = val;
        }
    }

Now we are annotating the custom attribute in the Test class. Here, we have to configure the CheckStatus value to true or false manually. In the FullDetails() method, we are accessing the custom attributes members by a foreach loop construct and later we check whether the status value is true or false.

[CheckStatus(false)]
    public class test
    {

        private string EmpName;
        private string Country;

        public test(string name, string country)
        {
            this.EmpName = name;
            this.Country = country;
        }
        public string FullDetails()
        {
            string str = null;
            Type type = this.GetType();

            CheckStatus[] attrib = (CheckStatus[])type.GetCustomAttributes(typeof(CheckStatus), false);

            if (attrib[0].status == true)
            {
                str = EmpName + "-" + Country;
            }
            else
            {
                str = "Hi " + EmpName;
            }

            return str;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            test obj = new test("Ajay","India");
            Console.WriteLine("Result:{0}",obj.FullDetails());
            Console.ReadKey();
        }
    }

After successfully compiling this code, the following figure depicting the output in both cases:

It is also possible to apply attributes on all types within a given module or assembly. To do so, simply add the attributes at assembly level in the AssemblyInfo.cs file. This file is a handy place to put attributes that are to be applied at the assembly level.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("CustomAttribute")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CustomAttribute")]
[assembly: AssemblyCopyright("Copyright ©  2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("ce5fc30b-e670-4115-aa64-4be10e7b6ea9")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Summary

This article examines the role and importance of attributes, which is an identical aspect of dynamic programming. When you adorn your types with attributes, the result is the expansion of the underlying assembly metadata. You've explored the different types of attributes and have gained an understanding of how to programmatically implement predefined attributes such as [WebMethod], [Serialization] etc. The prime objective of this article is to teach you how to create your own custom attributes as per the programming needs. After going through this article, we have a full understanding of how to apply both predefined and custom attributes over types.