Monday, 30 July 2012

Attributes In C#


Attributes


Attributes extend the capabilities of C# language. A .NET application contains code, data and metadata. Metadata is the information about the data, which contains information about the types, code, methods, assemblies and so forth. Attributes are a mechanism of adding metadata like some declarative information about methods, classes, objects to the program itself. They can be extracted using runtime services.
Attributes can be mentioned as annotations that a program can store and use. Attribute is an extended way to document a code. We can use attributes to define both design-level information (such as help file, URL for documentation) and run – time information (such as associating XML field with class field). We can also create ‘self – describing” components using attributes.

Uses Of Attributes

Attributes provide a power method of associating declarative information with C# code. Some of the uses of attributes are:
  • Attributes are used to indicate which field in a database a particular property should be written to.
  • Attributes are declarative information to various program entities which can be retrieved at run-time.
  • Attributes can be used to define both design-level information such as a help file or a URL for documentation and run-time information such as associating an XML field with a class field.
  • Attributes describe how to serialize data, specify characteristics that are used to enforce security, and limit optimization by the just-in-time compiler so the code remains easy to debug.

Types of Attributes


There are two types of attributes namely:
  • Built-in attributes
    The built-in attributes are a part of CLR and they are integrated into .NET. The .NET Framework is equipped with a number of built-in attributes that performs a host of functions. Some of the predefined attributes are:
    • STAThread
    • Conditional
    • DllImport
    • Obsolete
  • Custom attributes
    There are situations in which the predefined .NET Framework attributes does not satisfy a programmer’s requirements. In such a scenario, custom attributes can be created which can provide properties that allow to store and retrieve information.

Built In Attributes

The two types of general built-in attributes available in .NET Framework are:
  • Conditional
  • DllImport

“Conditional” Attribute

Conditional attribute can be used as debugging aid in the C# code. The C# conditional attribute basically allows you to control when a method should and should not be called. The benefit of the C# conditional attribute is that it is applied at the method level, which results in source code that is more readable. This attribute causes conditional compilation of method calls, depending on the value of the symbol that is defined. This method which is marked by a conditional attribute should always return void or else it will generate a compilation error. There are some restrictions on declaring a conditional method. They are:
  • This method must be a method in a class or in a struct.
  • This method must have a return type of void.
  • This method can be marked with the virtual modifier but not with the override modifier.
  • This method must not be an implementation of an interface method.
Example: The Conditional attribute ensures that the method GetName() will only be used in the debug versions of the solutions. This method will not execute in the release versions of the solutions.
using System;
using System.Diagnostics;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
string _name;
[Conditional ("DEBUG")]
public void GetName()
{
Console.WriteLine("Enter Name of the Employee: ");
_name = Console.ReadLine();
Console.WriteLine("Hi {0}. How are you!!", _name);
}
public static void Main()
{
Console.WriteLine("Before calling the method GetName()");
new Program().GetName();
}
}
}

DllImport Attribute

DLL is an acronym for Dynamic Link Library. DllImport attribute can be used to invoke unmanaged code in C# program. Unmanaged code is the code that has been developed outside the .NET environment. By using this attribute, unmanaged code can be invoked residing in the DLLs from a managed C# environment. The namespace System.Runtime.InteropServices should be imported in order to work with the DllImport attribute.
Example – User32.dll
class Coffeeparlour is a sealed class in this example

using System;
using System.Collections.Generic;
using System.Text;
namespace Coffee
{
sealed class CoffeeParlour
{
[DllImport("User32.dll")]
public static extern int MessageBox(int hParent, string message, string caption, int type);
private const decimal _basePrice = 1.55M;
private string[] _flavor;
private string[] _ingredient;
private int _cups;
private decimal _totalPrice;
public CoffeeParlour()
{
_flavor = new string[3];
_flavor[0] = "Coffee";
_flavor[1] = "Mocha";
_flavor[2] = "Caramel";
_ingredient = new string[3];
_ingredient[0] = "No Ingredient";
_ingredient[1] = "Chocolate Cream";
_ingredient[2] = "Caramel Cream";
}
public int ChooseFlavor()
{
int choice = 0;
for (int i = 0; i < _flavor.Length; i++) { Console.WriteLine("{0} - {1}", i + 1, _flavor[i]); } do { try { Console.Write("Your Choice? "); choice = int.Parse(Console.ReadLine()); if (choice < 1 || choice > _flavor.Length)
Console.WriteLine("Invalid Choice - Try Again!\n");
}
catch (FormatException)
{
Console.WriteLine("Please enter a valid number!");
}
catch (Exception)
{
Console.WriteLine("Wrong input");
}
} while (choice < 1 || choice > _flavor.Length);
return choice;
}

public int ChooseIngredient()
{
int choice = 0;
Console.WriteLine("\nDo you want an ingredient?");
for (int i = 0; i < _ingredient.Length; i++) { Console.WriteLine("{0} - {1}", i + 1, _ingredient[i]); } do { try { Console.Write("Your Choice? "); choice = int.Parse(Console.ReadLine()); if (choice < 1 || choice > _ingredient.Length)
Console.WriteLine("Invalid Choice - Try Again!\n");
}
catch (FormatException)
{
Console.WriteLine("Please enter a valid number!");
}
catch (Exception)
{
Console.WriteLine("Wrong input");
}
} while (choice < 1 || choice > _ingredient.Length);
return choice;
}

public void SpecifyNumberOfCups()
{
do
{
try
{
Console.Write("How many cups(1,2 or 3)?");
_cups = int.Parse(Console.ReadLine());
if (_cups < 1 || _cups > 3)
Console.WriteLine("Invalid Choice - Try Again!");
}
catch (FormatException)
{
Console.WriteLine("Please enter a valid number!");
}
catch (Exception)
{
Console.WriteLine("Wrong input");
}
} while (_cups < 1 || _cups > 3);
}

public void ProcessCustomerOrder()
{
int choiceFlavor;
int choiceIngredient;
decimal priceIngredient, priceCup;
Console.WriteLine("David's Coffee parlour");
choiceFlavor = ChooseFlavor();
choiceIngredient = ChooseIngredient();
SpecifyNumberOfCups();
if (choiceIngredient == 2 || choiceIngredient == 3)
priceIngredient = 0.50M;
else
priceIngredient = 0;
if (_cups == 1)
priceCup = 0.65M;
else if (_cups == 2)
priceCup = 1.05M;
else
priceCup = 1.55M;
_totalPrice = priceCup + priceIngredient;
DisplayReceipt(ref choiceFlavor, ref choiceIngredient);
}
[Conditional("DEBUG")]
public void DisplayReceipt(ref int flavor, ref int ingredient)
{
String str = "Flavor: " + Convert.ToString(_flavor[flavor - 1]);
str += "\nIngredient: " + Convert.ToString(_ingredient[ingredient - 1]);
str += "\nCups: " + Convert.ToString(_cups);
str += "\nTotal Amount: $" + Convert.ToString(_totalPrice);
MessageBox(0, str, "Coffee Order", 0);
}
}
}

Class Program
using System;
using System.Collections.Generic;
using System.Text;
namespace Coffee
{
class Program
{
static void Main(string[] args)
{
CoffeeParlour objCoffeeParlour = new CoffeeParlour();
objCoffeeParlour.ProcessCustomerOrder();
}
}
}

Obsolete

using System;
using System.Collections.Generic;
using System.Text;

namespace BuiltInAttr
{
class Program
{
[Obsolete("Don't use Old method, use New method", true)]
static void Old() { }
static void New() { }
static void Main(string[] args)
{
Old();
}
}
}

Custom Attributes

Custom attributes, like built-in attributes are objects that are associated with one or more programmatic elements. They are stored with the metadata of their associated elements, and provide mechanisms for a program to retrieve their values. E.g. a custom attribute can be created which would allow storing information about code modifications that is normally recorded as comments in a source code. The System.Attribute class represents the base class for custom attributes. This class provides all the necessary methods to retrieve custom attributes.

Custom Attributes – “AttributeUsage” Attribute


AttributeUsage attribute help to control the usage of custom attributes. The parameter to AttributeUsage contains values from the System.AttributeTarges enumeration to specify how the custom attribute can be used.
AttributeUsage has three properties that can be set by defining the parameters, ValidOn, AllowMultiple and Inherited. Defining or controlling Usage of Custom Attribute
1.
using System;
namespace ConsoleApplication1
{
class ParentAttribute : Attribute
{
protected String description;
public ParentAttribute(String Description_in)
{
this.description = Description_in;
}
public String Description
{
get
{
return this.description;
}
}
}
[Parent("this is a class")]
class Program
{
public static void Main() { }
}
}
2.
AttributeUsage is a pre – defined class that helps in controlling the usage of custom attributes. i.e. we can define attributes of custom attribute class. AttributeUsage has 3 properties which can be set while placing it on our custom attribute.
  • ValidOn: Through this property, we can define the program entities on which our custom attribute can be placed. The set of all possible program entities on which an attribute can be placed is listed in the AttributeTargets enumerator. We can combine several AttributeTargets using a bitwise OR operation.
  • AllowMultiple: This property marks whether our custom attribute can be placed more than once on the same program entity.
  • Inherited: We can control the inheritance rules of our attribute using this property. This property marks whether our attribute will be inherited by the class derived from the class on which we have placed it.

using System;
namespace ConsoleApplication1
{
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true,Inherited=false)]
class ParentAttribute : Attribute
{
protected String description;
public ParentAttribute(String Description_in)
{
this.description = Description_in;
}
public String Description
{
get
{
return this.description;
}
}
}
[Parent("this is a class")]
class Program
{
//[Parent("this is a class")]
public static void Main() { }
}
}

“AttributeUsage” - AttributeTargets.All


using System;
using System.Collections.Generic;
using System.Text;
namespace TokyoSoftwares
{
[AttributeUsage(AttributeTargets.All,Inherited =true,AllowMultiple=true)]
class CodeAttributes: Attribute
{
private string _developerName;
private string _reviewerName;
private string _developmentDate;
private string _versionNo;
private string _reviewDate;
private string _comment;
public CodeAttributes(string developerName, string reviewerName, string developmentDate, string versionNo, string reviewDate)
{
this._developerName = developerName;
this._reviewerName = reviewerName;
this._developmentDate = developmentDate;
this._versionNo = versionNo;
this._reviewDate = reviewDate;
}
public string DeveloperName
{
get
{
return _developerName;
}
}
public string ReviewerName
{
get
{
return _reviewerName;
}
}
public string ReviewDate
{
get
{
return _reviewDate;
}
}
public string VersionNo
{
get
{
return _versionNo;
}
set
{
_versionNo = value;
}
}
public string Date
{
get
{
return _developmentDate;
}
}
public string Comment
{
get
{
return _comment;
}
set
{
_comment = value;
}
}
}

[CodeAttributes("Allan John","Paul Nixon","01/03/10","1.1","03/03/10",Comment="First Review")]
[CodeAttributes("Allan John","Paul Nixon","04/03/10","1.2","06/03/10",Comment="Fixed error in Caluclations")]
[CodeAttributes("Allan John","Paul Nixon","07/03/10","1.3","08/03/10",Comment="Error Closed")]
class Employee
{
private string _name;
protected float BillingRate;
private string _designation;
public Employee(){}
public Employee(string name, float billingRate, string designation)
{
this._name = name;
this.BillingRate = billingRate;
this._designation = designation;
}
public virtual float CalculateSalary(float hours)
{
return (hours * BillingRate);
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace TokyoSoftwares
{
class Program
{
static void Main(string[] args)
{
Employee objManager;
objManager = new Employee("Smith", 600F, "Project Manager");
Console.WriteLine("Salary for Smith = {0}", objManager.CalculateSalary(2F));
Employee objManager1;
objManager1 = new Employee("Allan", 200.5F, "Software Developer");
Console.WriteLine("Salary for Allan = {0}", objManager1.CalculateSalary(2F));
MemberInfo objMemberInfo = typeof(Employee);
object[] attributes;
attributes = objMemberInfo.GetCustomAttributes(typeof(CodeAttributes), false);
Console.WriteLine("\n\tAttribute Information for class Employee");
foreach (Object attribute in attributes)
{
CodeAttributes objCodeInfo = (CodeAttributes)attribute;
Console.WriteLine("Developer Name: {0}", objCodeInfo.DeveloperName);
Console.WriteLine("Reviewer Name: {0}", objCodeInfo.ReviewerName);
Console.WriteLine("Date of Development: {0}", objCodeInfo.Date);
Console.WriteLine("Date of Review: {0}", objCodeInfo.ReviewDate);
Console.WriteLine("Version No. {0}", objCodeInfo.VersionNo);
Console.WriteLine("Comment: {0}\n", objCodeInfo.Comment);
}
}
}
}

Using Win32API


using System;
using System.Runtime.InteropServices;
namespace ConditionalTest
{
class TestWin32
{
[DllImport("User32.dll")]
public static extern int MessageBox(int hParent, string message, string caption, int type);
public static void Main(string [] args)
{
int counter = 1;
while (counter < 5) { Console.WriteLine("This is Line {0}", counter); counter++; if (counter == 3) { MessageBox(0, "Program Execution Stopped", "Message",0); break; } } } } }