Arrao4u

…a blog by Rama Rao

Archive for the ‘Attrubutes & Reflection’ Category

Attrubutes & Reflection

Posted by arrao4u on December 13, 2009

Usinng an attribute is a way to add special meaning to our method and cause it to act in a certain way

Attributes are a mechanism for adding metadata. Reflection is the process by which a program can read its own metadata. This article discusses both these concepts in detail with examples wherever necessary.

Attributes provide a powerful way to extend metadata by associating declarative information with C# code. The attribute information is stored with the metadata of the element and can be easily retrieved at runtime using reflection.

Attribute

An attribute is essentially an object that represents the data that is associated with a program element. The element to which an attribute is attached is referred to as the target of that attribute.

Attribute targets can be one of the following:

  • All
  • Assembly
  • Class
  • Constructor
  • Delegate
  • Enum
  • Event
  • Field
  • Interface
  • Method
  • Module
  • Parameter
  • Property
  • ReturnValue
  • Struct

Types of Attributes

Attributes are basically of two types, intrinsic and custom.

Intrinsic Attributes

Intrinsic attributes are supplied as part of the Common Language Runtime, and they are integrated into .NET.

In this example below we use a pre–defined attribute, Obsolete, which marks a program entity as obsolete. The attribute accepts two parameters of which the first is a message and the second a boolean value. If the second parameter is true, the compiler gives an error if the method is invoked, and a warning otherwise.

Listing 1: Using the Obsolete Attribute

using System;
public class Test
{
  [Obsolete("This method is deprecated. Usethe method Display(string) 
  instead.", false)]
  void Display()
  {
  }
  void Display(string s)
  {
  }
  public static void Main( ) 
  {
  Test test = new Test ();
  test.Display ();
  }
}

Custom Attributes

Custom attributes are attributes that we create for our own purposes. Attributes are public classes and are initialized using constructors. Every attribute must have at least one constructor. The constructors can be overloaded to allow the attribute to be applied to the class in multiple ways.

To create a custom attribute, we have to derive our new custom Attribute class from the class System.Attribute, as shown below.

Listing 2: Declaring a Custom Attribute Class

using System;
public class Comments : Attribute
{
}

Next, we have to specify the target of the attribute using the AttributeUsage attribute. An attribute is applied to the target by specifying the attribute name in brackets as shown in the example below.

Listing 3: Declaring the Custom Attribute Class

public class Comments : System.Attribute[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
AttributeUsage

The scope and target of the attribute can be defined by applying AttributeUsage. It contains three properties, which we can set to specify attributes.

AllowOn

This is a set of flags that indicates the program entities on which the attribute can be placed. Multiple AttributeTargets can also be specified using a bitwise OR operator.

AllowMultiple

AllowMultiple lets you specify whether you can apply multiple instances of a particular attribute to the same element. If AllowMultiple is set to true, then inherited classes will inherit all instances of the attribute from the parent class. The default value for AllowMultiple is false.

Inherited

The Inherited property determines whether the attribute will be inherited by classes that are derived from the classes to which the attribute is applied. The default value for Inherited is true, indicating that an attribute applied to the base class will be applied to all its derived classes.

Attribute Parameters

Attributes accept parameters for customization. They take two types of parameters, positional and named.

Positional Parameters: Positional parameters are specified using constructor arguments to the attribute class.

Named Parameters: Named parameters are defined by having a non-static field or property in the attribute class.

Attribute parameter types can be:

bool, byte, char, double, float, int, long, short, string

System.object

System.Type

A public enum

A one-dimensional array of the above types

Implementing a Custom Attribute class

The following section shows a custom attribute class called Comments, the application of the attribute to several entities of a class called Employee, and usage of reflection on this class to retrieve the attributes already specified. In the Comments class, the parameters author, type, and description are compulsory positional parameters and status is an optional named parameter.

Listing 4: Implementing the Custom Attribute Class

using System;
using System.Reflection; 
[AttributeUsage( AttributeTargets.All )]
public class Comments : System.Attribute
{
 public string author = String.Empty;
 public string type = String.Empty;
 public string description = String.Empty;
 private string status = String.Empty;
 public Comments(string author, string type,string description)
 {
  this.author = author;
  this.type = type;
  this.description = description;
 }
 public string Status
 {
  get
  {
   return status;
  }
  set
  {
   status = value;
  }
 }
 public static void DisplayAttributes(Type t) 
 {
  Comments comments = (Comments) Attribute.GetCustomAttribute(t,typeof   
  (Comments));
  Console.WriteLine("The Author is:{0}." , comments.author);
  Console.WriteLine("The Description is:{0}." , comments.description);
  System.Console.WriteLine("Class Name:{0}", t.Name); 
  MethodInfo[] methods = t.GetMethods(); 
  object[] attributes = null;
  for (int i = 0, l = methods.GetLength(0); i< l; i++) 
  {
   MethodInfo mi = methods[i];
   attributes = mi.GetCustomAttributes(true);
   foreach (Attribute attribute in attributes) 
   {
    if (attribute is Comments) 
    {
     Comments theComments = (Comments)attribute;
     System.Console.WriteLine("Name: {0} ,Type : {1}, Purpose: {2} , Created 
     By : {3}", mi.Name, theComments.type ,theComments.description, 
     theComments.author );
    } 
   }
  } 
 }
}
 
[Comments("Joydip","Class","Employee Entity")]
class Employee
{
 private string employeeName;
 private int basic;
 [Comments("Joydip","Property","Set/Retrieve name of the employee", Status = 
 "Complete")]
 public string Name
 {
  get 
  {
   return employeeName;
  }
  set
  {
   employeeName = value;
  }
}
 [Comments("Joydip","Property","Set/Retrieve basic salary of the employee",  
 Status = "Complete")]
 public int Basic
 {
  get
  {
   return basic;
  }
  set
  {
   basic = value;
  }
}
 [Comments("Joydip","Method","Display employee details", Status = 
 "Complete")]
 public void Display()
 {
  Console.WriteLine ("The name is"+basic);
  Console.WriteLine ("The basic is"+basic);
 }
 public static void Main(string[] args)
 {
  Employee employee = new Employee ();
  Comments.DisplayAttributes(typeof(Employee));
  System.Console.Read(); 
 } 
}

The method DisplayAttributes displays all the attributes set to the elements of the class Employee using reflection.

Reflection

Reflection provides objects that encapsulate assemblies, modules, and types. It is the process by which a program can inspect metadata information dynamically using the reflection API. Using reflection, we can create instances of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. Reflection is much the same as RTTI of native C++ but with a major difference in that reflection in C# works with managed code and is much more powerful.

The reflection classes are contained in the namespace System.Reflection. The classes in the Reflection namespace, along with the System.Type and System.TypedReference classes, provide support for examining and interacting with the metadata. The abstract base class Type helps access metadata information. The types include the constructors, methods, fields, properties, and events of a class and the module and the assembly in which these are stored.

The System.Reflection namespace defines the following types:

  • Assembly
  • Module
  • Enum
  • MethodInfo
  • ConstructorInfo
  • MemberInfo
  • ParameterInfo
  • Type
  • FieldInfo
  • EventInfo
  • PropertyInfo

Here is a complete example of a class that inspects another class and demonstrates the power of reflection to display the metadata information dynamically.

Listing 5: Reflection in Action

using System;
using System.Reflection;
namespace ReflectionTest
{
 public class Employee
 {
  string name;
  public string Name
 {
  get
  {
   return name ; 
  }
  set
  {
   name = value ; 
  }
}
 public Employee()
 {
 }
 public Employee ( string name )
 {
  this.name = name;
 }
 public void Display ()
 {
 }
}
public class Reflect
{
 public static void DisplayDetails(Type type)
 {
  Console.WriteLine ( "Class: " + type) ;
  Console.WriteLine ();
  Console.WriteLine ( "Namespace: " +type.Namespace ) ;
  Console.WriteLine ();
  ConstructorInfo[] constructorInfo = 
  type.GetConstructors( );
  Console.WriteLine( "Constructors:--") ;
  foreach( ConstructorInfo c in constructorInfo)
  {
   Console.WriteLine( c ) ;
  }
 
  PropertyInfo[] propertyInfo =type.GetProperties( );
  Console.WriteLine ();
  Console.WriteLine( "Properties:--" );
  foreach( PropertyInfo p in propertyInfo )
  {
   Console.WriteLine( p ) ;
  }
  MethodInfo[] methodInfo = type.GetMethods( ) ;
  Console.WriteLine ();
  Console.WriteLine( "Methods:--" ) ;
  ParameterInfo[] parameterInfo = null;
  foreach( MethodInfo m in methodInfo )
  {
   Console.WriteLine( "Method Name: "+ m.Name ) ;
   parameterInfo = m.GetParameters () ;
   foreach ( ParameterInfo p in parameterInfo )
   {
    Console.WriteLine("Parameter Type:" + p.ParameterType + " Parameter 
    Name: " + p.Name ) ;
   }
  } 
 }
  public static void Main ( string[] args )
  {
    DisplayDetails(typeof(Employee));
  } 
 }
}

Conclusion

C# is an imperative language, but it also offers scope for adding declarative features to the code and their later retrieval if necessary. We can use attributes to provide both design-level and run-time information to our code.

There are many attributes that come with DotNet.

Conditional attribute

By using the Conditional attribute, we are following a scenario that if a particular condition is true, the user will have access to that specific method. Suppose you wanted your specific method to run only if Internet Explorer is found on the system. Applying this type of security on a method was not easy in the past. Now, it is possible by applying conditional attributes.

Case 2

Suppose you want a method to run if program is in Debug mode. We will write the conditional statement as follows:

[Conditional(.DEBUG.)]
public static void Help(String str)
{
   Console.WriteLine(str);
}

If one calls this method in Main(), it will run only if the program is in Debug mode. If you turn it off in the release mode, it will not run.

DLL Import

Before DotNet, if one wanted to access the core Windows API, he could add a reference and use the library provided by Windows SDK. In DotNet, if we want to access those core features of DotNet, we use DllImport. Component DLLs aren’t accessed this way. They are accessed by making a reference to them. Normal DLLs are accessed by using the DllImport attribute.

Case 3

using System;
System.Runtime.InteropServices;
class Beeper
{
   [DllImport(.kernel32.dll.)]
   Public static extern bool Beep(int frequency,int duration);
   static void Main()
   {
      Beep(1000,111);
   }
}

So far, we have learned how to use attributes in DotNet. Still, we haven’t learn how to create our own custom attributes with specified behaviour that we assign.

Steps in Creating a Custom Attribute

  1. Define the attribute’s usage.
  2. Extend our class with AttributeClass.
  3. Define the behaviours to the class.

Attribute behaviours can be of these types:

The second step is to extend the class with the Attribute class. Basically, our attribute is a class that defines the behaviours of our attribute. For example:

...
public class MySpecialAttribute:Attribute
{...

A point to be noted here is that the name of Attribute we are going to make is MySpecial. We didn’t suffix the word Attribute after MySpecial. DotNet automatically suffixed it.

Let us create a sample custom attribute that will execute the method if the regKey provided to the Attribute parameter is correct:

using System;
namespace RegKeyAttributeTestor
{
   [AttributeUsage(AttributeTargets.Method|AttributeTargets.Struct,
                   AllowMultiple=false,Inherited=false]
   public class MyAttribute:Attribute
   {
      private string regKey="a12nf";
      public MyAttribute(string regKey)
      {
         if(this.regKey==regKey)
         {
            Console.WriteLine("Permitted to use this App");
         }
         else
         {
            Console.WriteLine("Not registered to use this App");
         }
      }
   }    //End Attribute class code
   class useAttrib
   {
      [MyAttribute("hello")]
      public static string SayHello(string str)
      {
         return str;
      }
      static void Main()
      {
         Console.WriteLine(SayHello("Hello to Sufyan"));
         Console.ReadLine();
      }
   }

}
AttributeUsage(AttributeTargets.Method|AttributeTargets.Struct,
               AllowMultiple=false, Inherited=false)]

Here, multiple Attribute targets are declared by using “|” between different targets. Allows multiple=false means multiple attributes can be used with this attribute. Inherited=false shows that if some class extends a class that uses this attribute and calls a method that is bound to this attribute, that class has no access to this attribute unless this property is set to true.

Reading Metadata from Assemblies

We first write our custom attribute:

using System;

namespace Sufyan
{
   [AttributeUsage(AttributeTargets.Method,AllowMultiple=false,
                   Inherited=false)]
   public class RegKeyAttribute : Attribute
   {
      private string regKey;

      public RegKeyAttribute(string VRegKey)
      {
         this.regKey = VRegKey;
         if (this.regKey==.hello.)
         {
            Console.WriteLine("Aha! You're Registered");
         }
         else
         {
            Console.WriteLine("Oho! You're not Registered");
         }
      }
   }
}

Now, we shall write our code to reference it through Assembly;.

Steps

First, compile the first example as a Class library; it will generate a .dll file. We will place this .dll file in the bin folder of our second example we are going to make now.

using System;
using System.Reflection;
using Sufyan;

namespace AdvancedDotNet1
{
   class Classic
   {
      static void Main(string[] args)
      {
         Assembly suf = Assembly.Load("RegKey");
         Type KIA=suf.GetType();
         []KO=Attribute.GetCustomAttributes(KIA);
         Object Regist =KO[0];
         Console.WriteLine("Registeration Code is :"+
                           Regist.ToString() );
         Console.ReadLine();
      }
   }
}

System.Reflection is used to retrieve info from the assembly metadata.

To use this in another class, verify that a specific method will execute if that particular developer is registered with me and I have issued him the license key to use.

class useAttrib
   {
      [RegKeyAttribute ("hello")]
      public static string SayHello(string str)
      {
         return str;
      }
      static void Main()
      {
         Console.WriteLine(SayHello("Hello to Sufyan"));
         Console.ReadLine();
      }
   }

Have a close look at this code. I have provided the Hello string as a parameter to the attribute. When we run this application, if we pass a string other then “hello”, it will say that you are not registered. We can further modify it according to our need.

Understanding Attributes in C#

Attributes are widely used in .Net libraries such as XML serialization. The syntax of attributes is very confusing until you realize that attributes are actually classes. This article provides a quick guide to understanding attribute syntax, how to read it, and how to look up an attribute documentation to be able to spcify them in your programming practice.

This article is not a thorough reference on attributes but a quick stab at the main features of attributes so that you can gain an understanding of attributes and start using them after the first read.

Attributes are used all over the place in .Net. Let us consider an XML serialization example where attributes take an important and necessary role. In this example we will be designing a c# class and stream it as XML. The c# class definition follow:


	Public class MyClass
	{
		public string myField = "abc";
	}

The above class can be streamed as XML as follows


	MyClass myObject = new MyClass;
	XmlSerializer  xs = new XmlSerializer(typeof(myObject));
	StringWriter sw = new StringWriter();
	Xs.serialze(sw,myObject);

This will produce an XML as follows


<MyClass>
	<myField>abc</myField>
</MyClass>

Let us now introduce attributes so that the generated XML can be tailored


	[XmlRoot("MyXmlClass")]
	Public class MyClass
	{
		[XmlElement(ElementName="myXmlField")]
		public string myField = "abc";
	}

This will produce an XML as follows


<MyXmlClass>
	<myXmlField>abc</myXmlField>
</MyXmlClass>

There are lot more attributes in XML serialization to control the serialization process. You can see these attributes in the references section of this article. But for now these two attributes are sufficient for our discussion.

When I have encountered these attributes first my first problem was figuring out how to read this attribute specification. What is the meaning of “[XmlRoot("somename")]“? I could guess that they are trying to set the name of the root node of the XML. But the syntax is unclear because in one case they are specifying a string directly inside quotes, in another case they are using some sort of a key to specify the string against.

Some times they use commas to separate multiple attributes, and sometimes they stack them. Sometimes there are repeated attributes and sometimes not. Sometimes they seem to be applied to fields and sometimes to classes. It seemed to be a very inconsistent set of rules.

But it turns out there is a mehtod to this madness afterall. The key to understanding to attributes lies in the fact that they represent .Net classes (first class citizens of the .net world). Let us investigate this aspect of attributes for a second.

ref:http://aspalliance.com/778_Attributes_and_Reflection_in_C

Posted in Attrubutes & Reflection, C# | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.