You are on page 1of 32

14

Attributes, The Reflection API And Conditionals

Attributes

a.cs

class zzz

public static void Main()

[vijay]

class yyy

Compiler Error

a.cs(7,2): error CS0246: The type or namespace name 'vijay' could not be found (are you missing
a using directive or an assembly reference?)

Anything in a square bracket is called an attribute. We tried to create an attribute called vijay,
which C#, for some reason, does not seem to recognize.

a.cs

class zzz
{

public static void Main()

class vijay : System.Attribute

[vijay]

class yyy

All that we have done is created a class vijay that has been derived from the class
System.Attribute and the error simply disappears. Thus an attribute is simply a class that derives
from System.Attribute. To understand attributes lets take an example with structures.

a.cs

class zzz

public static void Main()

yyy a = new yyy();

a.i = 65536+512+3;

System.Console.WriteLine(a.i + " " + a.j + " " + a.k);

}
}

struct yyy {

public int i;

public short j;

public byte k;

Output

66051 0 0

A simple revision once again. We have created a structure a, that looks like yyy and initialized
only one member i. Hence we see the warnings. The other members j and k get a default value of
zero.

a.cs

using System;

class zzz

unsafe public static void Main()

Console.WriteLine(sizeof(byte) + " " + sizeof(short) + " " + sizeof(int) + " " + sizeof(long));

>csc a.cs

Compiler Error

a.cs(4,27): error CS0227: Unsafe code may only appear if compiling with /unsafe

The error here says that you have to use the /unsafe option while compiling any unsafe code.
>csc a.cs /unsafe

Output

1248

We shall explain the modifier unsafe in the next chapter. Sizeof tells us how much memory C#
allocates for a data type. A byte is allocated one memory location, short 2, int 4 and a long 8.

a.cs

using System.Runtime.InteropServices;

class zzz

public static void Main()

yyy a = new yyy();

a.i = 65536+512+3;

System.Console.WriteLine(a.i + “ “ + a.j + “ “ + a.k);

[StructLayout(LayoutKind.Explicit)]

struct yyy

[FieldOffset(0)] public int i;

[FieldOffset(0)] public short j;

[FieldOffset(0)] public byte k;

Output
66051 515 3

We are using an attribute StructLayout that belongs to the namespace


System.Runtime.InteropServices. In the earlier program, we had used an attribute called vijay.
Thus, StructLayout is a class derived from Attribute. We are passing a parameter
LayoutKind.Explicit to it. The output now differs dramatically.

Every variable is stored in memory. FieldOffset indicates the starting position of the variable
within the memory location. Offset of 0 will position i, j, and k, all three variables at the same
memory address of a. Explicit requires FieldOffset to be mentioned as we are explicitly laying
the order for the variables held in the strucuture.LayoutKind.Sequential and LayoutKind.Auto
gives different memory locations to each of the variable.

We will explain the reasons a little later in the coming chapter ‘Unsafe Code’. We have seen how
important attributes are so lets delve deeper into them.

a.cs

class zzz

public static void Main()

class vijayAttribute : System.Attribute

[vijay]

class yyy

[vijayAttribute]
class yyy1

We are allowed a little leeway in the name of the attribute. By convention, the attribute class
should end with the word Attribute and when we use the attribute, the name attribute is optional.

a.cs

class zzz

public static void Main()

class vijay : System.Attribute

[vijay("hi")]

class yyy

Compiler Error

a.cs(10,2): error CS1501: No overload for method 'vijay' takes '1' arguments

We had used the attribute StructLayout earlier where we passed a parameter. When we do the
same thing with our attribute vijay, we get the above error.

a.cs
class zzz

public static void Main()

class vijay : System.Attribute

public vijay(string s)

[vijay("hi")]

class yyy

We forgot to add a constructor that accepts a string as a parameter. If we had passed a number to
our attribute vijay, we would have to create a constructor that accepts an int. Thus if we pass 2
parameters to vijay, we need the appropriate constructor.

a.cs

class zzz

public static void Main()

{
}

class vijay : System.Attribute

public vijay(string s ,int i)

[vijay("hi",10,mukhi = 200)]

class yyy

Compiler Error

a.cs(13,16): error CS0103: The name 'mukhi' does not exist in the class or namespace 'vijay'

What we tried to do is, take a word called mukhi and initialize it to 200. C# comes back and tells
us that it does not know what mukhi is.

a.cs

class zzz

public static void Main()

class vijay : System.Attribute


{

public vijay(string s ,int i)

public int mukhi;

[vijay("hi",10,mukhi = 200)]

class yyy

mukhi, now, is called a named parameter. It can also be termed as a property.

a.cs

class zzz

public static void Main()

class vijay : System.Attribute

public vijay(string s ,int i)

}
public int mukhi;

public string sonal

get

return "ss";

set

[vijay("hi",10, mukhi = 200, sonal = "bye")]

class yyy

A named parameter is a non-static field or a non-readonly property. A positional parameter is


what we pass on to a constructor. We have 2 positional parameters as our constructor has two
parameters and mukhi and sonal are our named parameters. The named parameters come after
the positional ones. The positional parameter's order is important, but the named parameters can
be in any order. If we don't follow this rule we will get an error as follows.

Compiler Error

a.cs(19,22): error CS1016: Named attribute argument expected

When we place the attribute before a function, the error disappears.


a.cs

using System.Runtime.InteropServices;

using System;

class zzz

public static void Main()

[AttributeUsage(AttributeTargets.Class)]

class vijay : System.Attribute

public vijay(string s ,int i)

public int mukhi;

public string sonal

get { return "ss"; }

set { ; }

class yyy
{

[vijay("hi",10, sonal = "bye",mukhi = 200 )]

public void abc() {}

Compiler Error

a.cs(24,2): error CS0592: Attribute 'vijay' is not valid on this declaration type. It is valid on
'class' declarations only.

AttributeUsage is one more attribute class derived from Attribute. It gives us the option to decide
where the user can use the Attribute. The parameter in this case is class and hence we can use it
only in front of a class and not in front of a method. The default is anywhere.

The Reflection API

Reflection or Introspection is when you look within to find out about your true self. In the same
way we need a method by means of which, our program can find out all about a class. We need
to know how many methods, properties etc while our program is executing or running. This
distinction is important and we could always read the documentation if we wanted to know more
about the functionality of a class. But, C# gives us a large number of functions that tell us the
innards of a class. These functions put together have to be used in a certain way. The functions
have to be called in a certain order and the parameters to them have to conform to certain data
types. This concept is called an API or a Application Program Interface. In short, an API is how
a programmer uses functions to get a desired result.

a.cs

using System;

class zzz

public static void Main()

Type m;

m = typeof(int);

System.Console.WriteLine(m.Name + " " + m.FullName);


m = typeof(System.Int32);

System.Console.WriteLine(m.Name + " " + m.FullName);

m = typeof(yyy);

System.Console.WriteLine(m.Name + " " + m.FullName);

class yyy

Output

Int32 System.Int32

Int32 System.Int32

yyy yyy

Typeof is a keyword. It needs a class name as a parameter. In the first case, we are specifying a
class called int. typeof returns an object that looks like Type. This class has two members Name,
which gives the name of the class and FullName which is the name preceded with the name of
the Namespace. When we use int as the name of the class, the member Name does not display int
but Int32; we mentioned earlier int is an alias for a structure Int32 in the System namespace. This
is what FullName tells us. We thus have visible proof that int is an alias for a structure. yyy is
class belonging to no namespace and hence the Name and FullName members are similar.

a.cs

using System;

using System.Reflection;

class zzz

public static void Main()


{

Type m = typeof(yyy);

MemberInfo [] n;

n = m.GetMembers();

Console.WriteLine(n.Length);

foreach ( MemberInfo a in n)

Console.WriteLine(a.Name);

class yyy {

public void abc() {}

private int pqr( int i ) { return 0;}

protected string xyz (string g , int p) {return "";}

Output

GetHashCode

Equals

ToString

abc

GetType
.ctor

We are now displaying the members of a class yyy. The class Type has a function called
GetMembers that returns an array of type MemberInfo. Every array has a field called Length that
returns the size of the array. In our specific case it is 6. We then use a foreach to run through
each member of the MemberInfo array and are displaying the name of each function using the
field Name from the class MemberInfo.

a.cs

using System;

using System.Reflection;

class zzz

public static void Main()

Type m = typeof(yyy);

MemberInfo [] n;

n = m.GetMembers();

Console.WriteLine(n.Length);

foreach ( MemberInfo a in n)

Console.WriteLine((MemberInfo)a + " " + a.DeclaringType);

class yyy {

public int i;
public void abc() {}

public int pqr( int i ) { return 0;}

public string xyz (string g , int p) {return "";}

Output

Int32 i yyy

Int32 GetHashCode() System.Object

Boolean Equals(System.Object) System.Object

System.String ToString() System.Object

Void abc() yyy

Int32 pqr(Int32) yyy

System.String xyz(System.String, Int32) yyy

System.Type GetType() System.Object

Void .ctor() yyy

The first concept you need to be clear with is that we can inspect only details of public members
and protected or private like pqr and xyz. Also variables are part of the members of a class. The
MemberInfo object has a ToString function that displays the entire function in all its glory
including parameters and their data types. The names of the parameter variables are however not
being displayed. The DeclaringType member returns the class name that the member belongs to.
Thus we can differentiate which class created the function.

Let us now display the attributes used on a class.

a.cs

using System.Runtime.InteropServices;

using System;

using System.Reflection;
class zzz

public static void Main()

Type m;

m = typeof(yyy);

System.Console.WriteLine(m.Name);

foreach(object a in m.GetCustomAttributes (true))

Console.WriteLine(a);

[AttributeUsage(AttributeTargets.All)]

class vijay : System.Attribute

string s1,s2;int i1;

public int mukhi;

public override string ToString()

return s1+" " + s2+" " + i1 + " " + mukhi;

public vijay(string s ,int i)

s1=s;i1=i;
}

public string sonal

get { return s2; }

set { s2 = value; }

[vijay("hi1",10, sonal = "bye1",mukhi = 200 )]

class yyy

[vijay("hi2",100, sonal = "bye2",mukhi = 2000 )]

public void abc() {}

[vijay("hi3",1000, sonal = "bye3",mukhi = 2 )]

public int i;

Output

yyy

hi1 bye1 10 200

GetCustomAttributes takes a boolean as parameter and returns an array of objects. The ToString
function of the attribute class gets called which will decide what string the attribute stands for.

a.cs

using System.Runtime.InteropServices;

using System;

using System.Reflection;
class zzz

public static void Main()

Type m;

m = typeof(yyy);

System.Console.WriteLine(m.Name);

foreach(MethodInfo a in m.GetMethods())

object [] b = a.GetCustomAttributes(true);

foreach(Attribute c in b)

Console.WriteLine(c);

[AttributeUsage(AttributeTargets.All)]

class vijay : System.Attribute

string s1,s2;int i1;

public int mukhi;

public override string ToString()


{

return s1+" " + s2+" " + i1 + " " + mukhi;

public vijay(string s ,int i)

s1=s;i1=i;

public string sonal

get { return s2; }

set { s2 = value; }

[vijay("hi1",10, sonal = "bye1",mukhi = 200 )]

class yyy

[vijay("hi2",100, sonal = "bye2",mukhi = 2000 )]

public void abc() {}

[vijay("hi3",1000, sonal = "bye3",mukhi = 2 )]

public void pqr() {}

Output

yyy
hi2 bye2 100 2000

hi3 bye3 1000 2

The object m looks like Type. As explained earlier, we are calling a function called GetMethods
which returns an array of MethodInfo's. a loops through each one. We have two methods and the
foreach gets executed twice. Once for abc and then for pqr. The GetCustomAttributes also exists
in a MethodInfo class that returns an array of objects representing our attributes. We iterate
through each, displaying what the ToString function returns. As we have only one attribute per
function, the second for each gets executed only once.

a.cs

using System.Runtime.InteropServices;

using System;

using System.Reflection;

class zzz

public static void Main()

Type m;

m = typeof(yyy);

System.Console.WriteLine(m.Name);

foreach(MethodInfo a in m.GetMethods())

object [] b = a.GetCustomAttributes(true);

foreach(Attribute c in b)

if ( c is vijay )

Console.WriteLine(c);
}

[AttributeUsage(AttributeTargets.All)]

class vijay : System.Attribute

string s1,s2;int i1;

public int mukhi;

public override string ToString()

return s1+" " + s2+" " + i1 + " " + mukhi;

public vijay(string s ,int i)

s1=s;i1=i;

public string sonal

get { return s2; }

set { s2 = value; }

}
[vijay("hi1",10, sonal = "bye1",mukhi = 200 )]

class yyy

[vijay("hi2",100, sonal = "bye2",mukhi = 2000 )]

public void abc() {}

[vijay("hi3",1000, sonal = "bye3",mukhi = 2 )]

public void pqr() {}

There is no change at all in the output. A function can be decorated with as many attributes as
you like. We would like to filter out certain attributes.

a.cs

using System.Runtime.InteropServices;

using System;

using System.Reflection;

class zzz

public static void Main()

Type m;

m = typeof(yyy);

System.Console.WriteLine(m.Name);

foreach(MethodInfo a in m.GetMethods())

object [] b = a.GetCustomAttributes(true);
foreach(Attribute c in b)

if ( c is vijay )

Console.WriteLine(c);

class vijay : System.Attribute

public override string ToString()

return "vijay";

class vijay1 : System.Attribute

public override string ToString()

return "vijay";

class yyy
{

[vijay()]

public void abc() {}

[vijay()]

[vijay1()]

public void pqr() {}

Output

yyy

vijay

vijay

We have two attribute classes vijay and vijay1. The function pqr has been decorated with 2
attributes whereas abc with only one. However we do not see vijay1 in the output as the 'c is
vijay' makes the if statement true only for the attribute vijay and not vijay1. For the function pqr
GetCustomAttributes returns an array of size two, but the if statement is true only for one of
them, the one with the attribute name vijay. This is because of the 'is'.

a.cs

using System;

class zzz

public static void Main()

yyy a = new yyy();

if ( a is yyy)

Console.WriteLine("a yyy");
xxx b = new yyy();

if ( b is xxx)

Console.WriteLine("b xxx");

if ( b is yyy)

Console.WriteLine("b yyy");

int d = 10;

if ( d is yyy)

Console.WriteLine("b yyy");

class xxx

class yyy : xxx

Output

a yyy

b xxx

b yyy

We would like to know the data type of an object at runtime. C# offers you a keyword 'is' that
lets you check the data type of an object. a looks like yyy and 'is' results in true. B looks like xxx
but is initialized to a new yyy. Thus it doubles up for a yyy and a xxx resulting in the next two
is's returning true. D is an int and not a yyy, so the last 'is' is false.

Attributes Revisited
Positional parameters are a must whereas names parameters are optional. Attribute parameters
can be a bool, byte, char, short, int, long, float and double. These are the simple types that C#
supports. Other data types are string, enums, objects arrays etc.

Attribute usage has a position parameter which specifies the elements where the attribute can be
used. The default is All. It also has one named parameter called AllowMultiple.

a.cs

using System.Runtime.InteropServices;

using System;

class zzz

public static void Main()

[AttributeUsage(AttributeTargets.All)]

class vijay : System.Attribute

public vijay(string s)

class yyy

[vijay("hi")][vijay("hi1")]

public void abc() {}


}

Compiler Error

a.cs(18,15): error CS0579: Duplicate 'vijay' attribute

By default we cannot use the same attribute twice on any entity.

a.cs

using System.Runtime.InteropServices;

using System;

class zzz

public static void Main()

[AttributeUsage(AttributeTargets.All,AllowMultiple=true)]

class vijay : System.Attribute

public vijay(string s)

class yyy

[vijay("hi")][vijay("hi1")]
[vijay("hi2") , vijay("hi3")]

public void abc() {}

We get no error as by default the AllowMultiple named parameter has a value of false. If we set
its value to true, we are allowed to use multiple attributes on any entity. The above two forms are
similar and either one can be used. Attribute permits us to set declarative information for various
program entities for use by someone else at run time.

Conditionals

a.cs

using System.Diagnostics;

using System;

class zzz

public static void Main()

yyy a = new yyy();

a.abc();

class yyy

[Conditional("vijay")]

public void abc()

Console.WriteLine("abc");
}

When we run the above program we get no output at all. In other words the function abc does not
get called at all. This is inspite of writing a.abc().

a.cs

#define vijay

using System.Diagnostics;

using System;

class zzz

public static void Main()

yyy a = new yyy();

a.abc();

class yyy

[Conditional("vijay")]

public void abc()

Console.WriteLine("abc");

}
Output

abc

Any line beginning with a # is read by the C# pre-processor, a program that starts before the C#
compiler starts. It has words like #define which creates a variable or word called vijay. In a
programming language, a variable has to have a value, but in the preprocessor scheme of things,
it may/maynot have a value. However if we do not give it a value, like in this case, the variable is
only set to have been defined or created. Anything in [] brackets is an Attribute class. Earlier we
had not created a variable vijay and hence the entire code of abc was left out of the executable
file. Not only that, but all calls to function abc were eliminated from our code. All of this by
defining or not defining a variable vijay. This is what we passed as the attribute to Conditional.
We can create functions that are omitted during compilation depending upon a preprocessing
symbol.

When we write code, we add a lot of code for debugging purposes. This code is to help the
programmer debug code. After a function works, it is error free, we do not require any of this
debugging code. One way to eliminate this debugging code is by making the functions
conditional. The resulting code is called a 'Retail build' and the debugging version, obviously a
'Debug build'. A Retail build is much smaller in size and obviously much faster. The #define has
to be at the start of code.

Another way of achieving the same result is by eliminating the #define from the code and
creating a preprocessor symbol as an option to the compiler.

>csc a.cs /d:vijay

The compiler option is /d and the colon is part of the syntax. Following the colon is the name of
the preprocessor symbol. In this case, it is vijay. We’ve discussed preprocessors in one of the
earlier chapters.

a.cs

using System;

class zzz

public static void Main()

yyy a = new yyy();

a.abc();
}

class yyy

[Obsolete]

public void abc()

Console.WriteLine("abc");

Compiler Error

a.cs(7,1): warning CS0612: 'yyy.abc()' is obsolete

Output

abc

Many a times we create functions in a class which we would not want the user to use, as these
functions were useful years ago, but are now obsolete. The only way to warn the user that some
time in the future we will no longer support these functions is by marking them with the attribute
Obsolete. We see the warning as displayed above but the program runs as normal.

You might also like