Language Extensions in C# 2.0

As we all know the final version of Visual studio 2005 will release shortly, its the time to know the new features of C#. Now C# has upgraded to v2.0 in Visual Studio 2005. It introduces several language extensions, I am going to covers few most important language extensions as below

  1. Generics
  2. Iterators
  3. Partial Class
  4. Nullable Types
  5. Anonymous Methods
  6. Global Alias Operator (:: Operator )
  7. Static Class
  8. Fixed Size Buffers
  9. Friend Assemblies
  10. #pragma


This article intends to the C# 1.x developers and covers what-why-how the new enhancement. The philosophy behind the document is to have quick update on C# 2.0 and leverage this in your day to day development work.

1. Generics

Generics is one of the great feature of C# 2.0 . it has lots of benefits and I am going to cover all major points of generics.

Generics is a mechanism that encapsulate operations that are not specific to any particular data type, that is it allows classes or structs or interfaces or methods to be parameterized by the types of data they store and manipulate. Generics provides the benefits like reusability, type safety and efficiency in a way that their non-generic counterparts cannot. Generics are most commonly used with collections and the methods that operate on them. The .NET framework 2.0 provides a new namespace System.Collections. Generics which contains several new generics based collection classes apart from this you can also create custom generic types and methods to provide your own generalized solutions that are type-safe and efficient.

If we look at the collection classes of .NET Framework 1.x there are certain limitations like it needs to perform boxing/unboxing or up-casting/down-casting while storing and retrieving of values for example if we store value type in ArrayList it performs boxing/unboxing or if store string in ArrayList it performs up-casting and down-casting because it takes System.Object as parameter , the other limitation is lack of compile-time type checking; since an ArrayList casts everything to Object to over come the above problems in .NET framework 1.X we can write our own type specific collection class ( custom collection class) but again such class are not generic ( reusable) for more than one data type and you have to rewrite different custom class for each data types.

The ultimate solutions to this problems is ArrayList needs a type parameter. That is precisely what generic provides. The namespace for generics is System.Collections.Generic , following code sample demonstrates how to work with generic List<T> .

List<int> mylist = new List<int>();
//avoid boxing/unboxing ,avoid up-casting/down-casting
mylist.Add(99);
//Compile-time error if you add any other type
mylist.Add("Wrong type passed");
Similarly you can define your custom generic
class for example
public class MyList <T>
{
T[] items;
int counter;
public void Add ( T item)
{
// code here
}
public void Remove (T item)
{
// code here
}
}

In the above example generics provide a facility for creating types that have type parameters ( MyList class with type parameter T). While instantiating MyList<T> specify the type for which they are created and store data of that type without conversion to and from System.Object. Actually the type parameter <T> acts as a placeholder until an actual type is specified at use as below.

MyList<int> objList = new MyList<int>();
objList.Add(3);
// this eliminate boxing/unboxing or casting operation

Its a compile time error if we add text to this list fro example

objList.Add("This is a test message");//compile-time error

The above example demonstrate only one type , Generic type declarations may have any number of type parameters lets say for hashtable we need key and value for this you need to specify the number of parameters while define the class as below

public class MyHash<T Key,T Value>
{
//reset of the code here
}

Now I am talking about how generic type instantiate and generics in the base class library 2.0 and constraints.

Generic Type Instantiation

Similar to non-generic type the IL code maintain special instruction for generic type .The first time an application creates an instance of a constructed generic type, such as myList<int>, the just-in-time (JIT) compiler of the .NET Common Language Runtime converts the generic IL and metadata to native code, substituting actual types for type parameters in the process. Subsequent references to that constructed generic type then use the same native code. The process of creating a specific constructed type from a generic type is known as a generic type instantiation and one important thing is The .NET Common Language Runtime creates a specialized copy of the native code for each generic type instantiation with a value type, but shares a single copy of the native code for all reference types because at the native code level, references are just pointers with the same representation.

Generics in the Base Class Library

Version 2.0 of the .NET Framework base class library provides a new namespace, System.Collections.Generic, which includes several ready-to-use generic collection classes and associated interfaces. These classes and interfaces are more efficient and type-safe than the non-generic collection classes provided in earlier releases of the .NET Framework. Before designing and implementing your own custom collection classes, consider whether you can use or derive a class from one of the classes listed as below

Collection<T> / ICollection<T> : Provides the base class for a generic collection and the correspondence non generics type is CollectionBase/ ICollection

Comparer<T>/ IComparer<T>/ IComparable<T> : Compares two objects of the same generic type for equivalence and for sorting and the correspondence non generics type is Comparer/IComparer/IComparable

Dictionary<K, V>/IDictionary<K,V> : Represents a collection of key/value pairs that are organized based on the key and the correspondence non generics type is Hashtable/IDictionary

Dictionary<K, V>.KeyCollection : Represents the collection of keys in a Dictionary<K, V>.

Dictionary<K, V>.ValueCollection : Represents the collection of values in a Dictionary<K, V>.

IEnumerable<T>/IEnumerator<T> : Represents a collection that can be iterated using foreach and the correspondence non generics type is IEnumerable/IEnumerator

KeyedCollection<T, U> : Represents a keyed collection. and the correspondence non generics type is KeyedCollection

LinkedList<T> : Represents a doubly linked list

LinkedListNode<T> : Represents a node in a LinkedList<T>

List<T> /IList<T> : Implements the IList<T> interface using an array whose size is dynamically increased as required and the correspondence non generics type is ArrayList /IList

Queue<T> : Represents a first-in, first-out collection of objects and the correspondence non generics type is Queue

ReadOnlyCollection<T> : Provides the base class for a generic read-only collection and the correspondence non generics type is ReadOnlyCollectionBase

SortedDictionary<K, V> : Represents a collection of key/value pairs that are sorted by key based on the associated IComparer<T> implementation and the correspondence non generics type is SortedList

Stack<T> : Represents a simple last-in-first-out (LIFO) collection of objects and the correspondence non generics type is Stack

Note :Dictionary<K, V>.KeyCollection,Dictionary<K,V>. ValueCollection, LinkedList<T>, LinkedListNode<T> does not has correspondence non generics type. The new generic class is slightly different from, and not fully compatible with, that of the non-generic class they replace.

One of the interesting part of Generics is constraints

Constraints

Apart from store data based on type parameter it also examine an item in the list to determine whether is it valid or needs to compare it to some other item the compiler must have some guarantee that the operator or method it needs to call will be supported by any type argument that might be specified by client code. This guarantee is obtained by applying one or more constraints to your generic class definition .for example

public class MyList <T>
{
public void Add ( T item)
{
if ( T.CompareTo(x) <0) // its a compile type error
{
}
}

As I mention its a compile time error because the type argument specified for T could be any type, the only members that can be assumed to exist on the item parameter are those declared by type object, such as Equals, GetHashCode, and ToString; a compile-time error therefore occurs in the above example. Constants tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called within the generic class. Constraints are applied using the contextual keyword where. The following code example demonstrates the functionality we can add to the MyList<T> class by applying a base class constraint

public class MyList <T> where T:IComparable
{
public void Add ( T item)
{
if ( T.CompareTo(x) <0) // its a compile type error
{
}
}

The constraint enables the generic class to use the IComparable property since all items of type T are guaranteed to be either IComparable type or an object that inherits/implements IComparable. Multiple constraints can be applied to the same type parameter, and the constraints themselves can be generic types as below

public class MyList <T> where T:IComparable, IList<T>
{.......}

The current version of C# 2.0 support Five types of constraints like

Where T: struct : The type argument must be a value type
Where T : class : The type argument must be a reference type
where T : new() : The type argument must have a public parameterless constructor. When used in conjunction with other constraints, the new() constraint must be specified last
where T : <base class name> : The type argument must be or derive from the specified base class
where T : <interface name> : The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.

By constraining the type parameter, you increase the number of allowable operations and method calls to those supported by the constraining type and all types in its inheritance hierarchy. Therefore, when designing generic classes or methods, if you will be performing any operation on the generic members beyond simple assignment or calling any methods not supported by System.Object, you will need to apply constraints to the type parameter.

When a type parameters that have no constraints, such as T in public class MyList<T>{...}, are called unbounded type parameters. The unbound parameters has below rules

  1. The != and == operators cannot be used because there is no guarantee that the concrete type argument will support these operators.
  2. They can be converted to and from System.Object or explicitly converted to any interface type.
  3. You can compare to null. If an unbounded parameter is compared to null, the comparison will always return false if the type argument is a value type

When a generic type parameter is used as a constraint, it is called a naked type constraint. Naked type constraints are useful when a member function with its own type parameter needs to constrain that parameter to the type parameter of the containing type, as shown in the following example:

class List<T>
{
//...
void Add<U>(List<U> items) where U:T {...}
}

In the previous example, T is a naked type constraint in the context of the Add method, and an unbounded type parameter in the context of the List class

Naked type constraints can also be used in generic class definitions. Note that the naked type constraint must also have been declared within the angle brackets along with any other type parameters

//naked type constraint
public class MyClass<T,U,V> where T : V

The usefulness of naked type constraints with generic classes is very limited because the compiler can assume nothing about a naked type constraint except that it derives from System.Object. Use naked type constraints on generic classes in scenarios in which you wish to enforce an inheritance relationship between two type parameters.

I conclude generics by talking few cool stuffs of generics class and differences between generics and C++ template.

Generic classes encapsulate operations that are not specific to any particular data type. In general the generic class are used for working with collection like list, hashtable, stacks or queue etc where the items are store or retrieve or remove regardless of the type of data. Generic class declarations follow the same rules as normal class declarations except where noted, and particularly with regard to naming, nesting and the permitted access controls. Generic class declarations may be nested inside non-generic class declarations for example class MyList<T> {}. One interesting thing about static field in generic class is A static variable in a generic class declaration is shared amongst all instances of the same closed constructed type but is not shared amongst instances of different closed constructed types. These rules apply regardless of whether the type of the static variable involves any type parameters or not for example

class C<T>
{
static int count = 0;
public C()
{
count++;
}
public static int Count
{
get { return count; }
}
}
class Application
{
static void Main()
{
// same closed constructed type because count is int type.
C<int> x1 = new C<int>();
Console.WriteLine(C<
int>.Count); // Prints 1
//different closed constructed types
C<double> x2 = new C<double>();
Console.WriteLine(C<
int>.Count); // Prints 1
// same closed constructed type because count is int type
C<int> x3 = new C<int>();
Console.WriteLine(C<
int>.Count); // Prints 2
}
}

The other cool stuff of generic class is default keyword .In generic classes and methods, one issue that arises is how to assign a default value to a parameterized type <T> because the value can be reference type or value type .So T = null is only valid if <T> is a reference type and T = 0 will only work for numeric value types but not for structs. The solution is to use the default keyword, which will return null for reference types and zero for numeric value types. For structs, it will return each member of the struct initialized to zero or null depending on whether they are value or reference types. The following example from the MyList<T> class shows how to use the default keyword.

public class MyList <T> where T:IComparable
{
public void Add ( T item)
{
T temp =
default(T);
}
}

There are lots to talk on generic class let me stop here with an assumption that lets the readers explore the other features of generic class ,method and interface .

Now I conclude generics with explaining the differences C++ Templates and C# Generic.

C# Generics and C++ templates are both language provide support for parameterized types. C# generics are a simpler approach to parameterized types without the complexity of C++ templates. The following are the differences between C# generics and C++ templates

  1. C# generics do not provide the same amount of flexibility as C++ templates. For example, it is not possible to call arithmetic operators in a C# generic class, although it is possible to call user defined operators.
  2. C# does not allow non-type template parameters, such as template C<int i> {}.
  3. C# does not support explicit specialization; that is, a custom implementation of a template for a specific type.
  4. C# does not support partial specialization: a custom implementation for a subset of the type arguments.
  5. C# does not allow the type parameter to be used as the base class for the generic type.
  6. C# does not allow type parameters to have default types
  7. In C#, a generic type parameter cannot itself be a generic, although constructed types can be used as generics. C++ does allow template parameters.
  8. C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and - on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.

2. Iterators

As we all familiar about C# foreach , it allow to iterate over the elements of a collection of enumerable ,the GetEnumerator method returns the enumerator from the collection. If we look enumerator has certain constraints like performance, implementations etc , Iterators makes this task more simple and efficient .

An iterators is a block that specifies executable code to return sequence of values in a chronological order that is Iterators code specifies how return values are generated when the foreach loop accesses each element of the collection. I mean Iterators simplify the process of implementing IEnumerable or IEnumerator methods. Iterators keeps track of current element in the collection and the job of the developers is to concentrate on get the values returns by the iterators. In Iterators the values are returns based on something called Yield. The prime job of yields is to provide a value to the enumerator object and it should be use inside a iterator block which might be used as a body of a method or a operator. An Iterators has certain restrictions like unsafe code is not allowed inside the method body or operators or the parameters of the methods should not be ref or out and yields should not appear inside the finally block. Similarly an iterators is distinguished from a normal statement block by the presence of one or more yield statements such as yield return statement produces the next value of the iteration and yield break statement indicates that the iteration is complete.

An iterator may be used as the body of a function member as long as the return type of the function member is one of the enumerator interfaces or one of the enumerable interfaces:

  • The enumerator interfaces are System.Collections.IEnumerator and types constructed from System.Collections.Generic.IEnumerator<T>.
  • The enumerable interfaces are System.Collections.IEnumerable and types constructed from System.Collections.Generic.IEnumerable<T>.

It is important to understand that an iterator is not a kind of member, but is a means of implementing a function member. A member implemented via an iterator may be overridden or overloaded by other members which may or may not be implemented with iterators.

Look at the below example this is a simple collection class that stores the days of the week as strings. For each iteration of a foreach loop, a different day of the week string is returned.

public class List
{
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while(counter++ < exponent)
{
result = result * number;
yield
return result;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:
foreach(int i in Power(2, 8))
Console.Write("{0} ", i);
}
}

The out put is 2 4 8 16 32 64 128 256

In the above example the yield keywords used to return the value(s). When the yield return statement is reached, the current location is stored. Execution is restarted from this location the next time the iterator is called. The iterator block must be called multiple times (seven times in this example) before its own for loop finishes returning all its values. This is what the calling foreach does. Iterators are especially useful with collection classes, providing an easy way of iterating non-trivial data structures such as binary trees. The below are the some of the important points to be keep in mind

  1. An iterator is a section of code that returns an ordered sequence of values.
  2. An iterator can be used as the body of a method, an operator, or a get accessor.
  3. The interator code uses the yield return statement to return each element in turn.
  4. Using iterators, it is no longer necessary to implement the interfaces System.Collections.IEnumerable and System.Collections.IEnumerator when creating a collection class that supports foreach. The compiler does this work for you.
  5. The return type of an iterator must be System.Collections.IEnumerable, System.Collections.IEnumerator or one of the generic iterator interfaces

The other feature of iterators is you can name give name to the iterators such as MyIterator lets rewrite the previous example with named iterators.

public class List
{
public IEnumerable<T> MyIterator(int number, int exponent)
{
int counter = 0;
int result = 1;
while(counter++ < exponent)
{
result = result * number;
yield
return result;
}
}
static void Main()
{
List myList =
new List();
// Display powers of 2 up to the exponent 8:
foreach(int i in myList.MyIterator(2, 8))
Console.Write("{0} ", i);
}
}

Likewise you can use iterators with Generics for example creating iterators block for Generic list as below

using System;
using System.Collections.Generic;
class MyGenericClass<T>
{
T[]
value;
public void AddElement(T[] t)
{
value = t;
}
public IEnumerator<T> GetEnumerator()
{
foreach(T t in value)
yield
return t;
}
}
class MainClass
{
static void Main()
{
MyGenericClass<
int> myGenericObject = new MyGenericClass<int>();
myGenericObject.AddElement(
new int[]{1,3,5,7,9});
Console.WriteLine("The elements of \"myGenericObject\" are:");
foreach(int i in myGenericObject)
{
Console.WriteLine(i);
}
}
}

The output is
1 3 5 7 9

3. Partial class

Partial class is one of the language enhancement in Visual Studio 2005. Partial class means the class definition can be split into multiple physical file (.cs or .vb files) each source file contains a section of class definition and at compile time compiler group together all partial classes and treats them as single entity .The benefits of partial class are

  1. It spreads the class into multiple files and allows the developers to work on different part of the class instead of sharing the same physical file we often face this problem while working in large project.
  2. It separates the business logic and system generated code ( code generated when creating windows form or web service wrapper code ).This helps debugging easier because the designer-generated code is separates from business logics and prevent developers from messing both the code.

To split the class use the partial keyword for example

//PartialDemo1.cs
partial public class MyClass
{
public TestMethod1(){}
}
//PartialDemo2.cs
partial public class MyClass
{
public TestMethod2(){}
}

Note: Partial class does not make any difference to the compiler , during compile time it simply groups all the partial classes and treats them a single entity (class)

There are few points one should register in his/her mind while working with partial class as below

1. Partial type definition must be in the same assembly / module (dll or exe) and it can not span multiple modules
 
2. All partial type definition must be meant to be parts of the same type and must be specified with partial keyword for example

partial public class MyClass
{
}
public class MyClass{} // generates compile time error

3. Generics type can be partial the class name and generic type parameters must match for example

partial public class MyList <T>
{
T[] items;
int counter;
public void Add ( T item)
{
// code here
}
}
partial
public class MyList <T>
{
public void Remove (T item)
{
// code here
}
}

Note: You can also specify the constraints (where clauses) along with partial type definition

4.Any accessibility modifier is allow with partial type definition like

public/private/protected/internal

5. Nested partial type allowed in partial type definition for example

//Myclass1.cs
partial class Myclass
{
partial
class MyNested{}
}
//Myclass2.cs
partial class Myclass
{
partial
class MyNested{}
}
6. Attributes of partial classes are merge at compile time
for example

//Myclass1.cs
[XYZ]
partial
class Myclass{}
//Myclass2.cs
[ABC]
partial
class myclass{}
this is equal to
[XYZ]
[ABC]
partial
class myclass{}

Note : Other merged attributes are XML Comments, Interfaces, Generics type parameter attributes, class attributes , members.

4 Nullable types

If we look at the existing Framework 1.X one of the main different between reference type and value type is reference type supports null value that means if you do not want to the reference type ( such as string, class) to refer any value by simply assign null (Nothing in VB.NET) to it where as the value type always contains a value. One interesting thing about value type is even though if you explicitly pass null to it , value type just gets assigned to its default value let say if you assign null to integer , it just assigned to Zero. The Framework 2.0 allows to assign null to variable type its called Nullable types.

The next question is what is the benefits of nullable types , to answer this question let me explain one example let say you are interacting with a database column called Employee.Salary , in some scenario it stores NULL ( salary of retired employee) , if you capture this value to a integer it stores zero that means it misleads that still the employee is working without salary but for this we have System.Data.SqlTypes namespace But those types do a lot of extra SQL-type stuff. Wouldn't it be nice to have this for all value types. There are few other ways to assign null to primitive data type like boxed value type this is not strongly-typed at compile-time, and involves doing a heap allocation for every type or a class wrapper for the value type. This is strongly-typed, but still involves a heap allocation, and the you have to write the wrapper which is not recommended and involved performance issues. One best solution to this problem is Nullable Type.

Nullable types address the scenario where you want to be able to have a primitive type with a null value. Nullable types are constructed using the ? type modifier. For example, int? is the nullable form of the predefined type int. A nullable type's underlying type must be a value type. Now how it is possible ? yes the answer is because of Generics for example

// Nullable Type Example
int? x;
if (x != null)
Console.WriteLine(x.Value);
else
Console.WriteLine("Undefined");

If we look at the design of nullable type in VS2005 , its nothing but a nullable type is a structure that combines a value of the underlying type with a boolean null indicator. An instance of a nullable type has two public read-only properties: HasValue, of type bool, and Value, of the nullable type's underlying type. HasValue is true for a non-null instance and false for a null instance. When HasValue is true, the Value property returns the contained value. When HasValue is false, an attempt to access the Value property throws an exception as below .

struct Nullable<T>
{
public bool HashValue;
public T Value;
}

You can use this struct directly, but we have also added some shortcut syntax to make the resulting code much cleaner. The first is the introduction of a new syntax for declaring a nullable type. Rather than typing:

Nullable<int> x = new Nullable<int>(125);

You can write :
int? x = 125; // more simplified way of working

Likewise if I wanted to add two nullable ints together and preserve null then here is the sample code
int? x = 125;
int? y = 33;
int? z = x + y;

Lets have quick recap the properties of of Nullable type

  1. Nullable types are used to create variables that can contain an undefined state.
  2. The syntax T? is shorthand for System.Nullable<T>, where T is a data type. The two forms are interchangeable.
  3. The HasValue property returns true if the variable contains a value, or false if it is null.
  4. The Value property returns a value if one is assigned, otherwise a System.InvalidOperationException is thrown.
  5. The default value for a nullable type variable sets HasValue to false. The Value is undefined

5. Anonymous Method

Anonymous method is one of the new feature of C# 2.0, as the name implies that it is a nameless method call by a delegate that mean it is a mechanism to pass a block of code as a parameter .Anonymous method reduce the coding overhead in instantiating delegates by eliminating the need to create a separate method , increase the usability and code maintainability. For example in .NET Framework 1.X to invoke a delegate we attach a method to it as below

partial class MyClass
{
delegate void MyDlelgate();
public void TestMessage()
{
MessageBox.Show("Hello User group");
}
public void CallDelegate()
{
MyDelegate myDel=
new MyDelegate(TestMessage);
MyDel();
}
}

Now lets rewrite the above code to demonstrate anonymous method

partial class MyClass
{
delegate void MyDlelgate();
public void CallDelegate()
{
MyDelegate myDel=
delegate() {MessageBox.Show("Hello User group")};
);
MyDel();
}
}

Anonymous method is defined in-line and not as a member of the class while invoking the delegate. Anonymous method can be use anywhere that a delegate is expected (you can pass an anonymous method into any method that accepts the appropriate delegate type as a parameter). One very good example is while creating a thread contains the code that the thread executes without creating an additional delegate method as below

Thread myThread = new Thread(delegate()
{
//code here
}

myThread.Start();

The syntax of Anonymous method is
 
delegate (parameter list) {block of code}.

Note: The scope of the parameter is the anonymous-method-block

The following are the few important point you should keep in mind while working with anonymous method

  1. Unsafe code can not be accessed with in anonymous block.
  2. It is an error to have statement like goto, break or continue inside the anonymous block whose target is outside the block.
  3. It is also an error to have the above statements outside the anonymous block whose target is inside the block.
  4. Anonymous method can not access the ref or out parameters of an outer scope.
  5. When defining an anonymous method with parameters, you define the parameter types and names after the delegate keyword just as if it were a conventional method. The method signature must match the definition of the delegate to which it is assigned.
  6. The parameter list of a delegate is compatible with an anonymous method if anonymous method has no parameter list and the delegate has no out parameters or anonymous method includes a parameter list that exactly matches the delegate's parameters in number, types, and modifiers.
  7. The return type of a delegate is compatible with an anonymous method if The delegate's return type is void and the anonymous method has no return statements or only return statements with no expression or the delegate's return type is not void and the expressions associated with all return statements in the anonymous method can be implicitly converted to the return type of the delegate.

6. Global Alias Operator

This is called namespace alias qualifier (:: operator) , some time when we added a type or assembly to another assembly and the names of those types or namespaces may conflict with names because that are already in use in the current assembly. For example

class MyClass
{
public int Colsole=7;
static void Main()
{
Console.WriteLine("Test Message");
// compile time error
}
}

If we observe Console.WriteLine cause compile time error because the System namespace hidden by MyClass. Some time we need to compromise with the variable name in order to over come with this problem but in C# 2.0 it can be resolve using Global Alias Operator like global::Console.WriteLine("Test Message").The key point is when the left identifier is global the search for right identifier starts at the global namespace and you can specify your class as global space like class MyClass: global::MyParent .The next question is what about defining or creating your own namespace called System the answer is it is strongly recommend that we should not create namespace called system the global namespace qualifier is your guarantee that you are calling the root namespace. the ::operator is useful in a large project its very rare but some time we face that the namespace duplication may occur in one to other form.

7. Static class

In C# 1.X if all the members and methods of a class are static that can not be instantiated i.e. having a pubic constructor does not make any sense , to prevent instantiation of such class we have to make the constructor as private and one important thing is class has to mark as sealed because static members are not meant for inheritance .In C# 2.0 static class makes the above tasks more safe and convenient way, static class mark the class as sealed with no constructor and its a compile time error if the you instantiate the static class. The main problems of earlier approach ( C# 1.X) is the private constructor takes up space in the IL actually its not a big issue but has a little effects or accidentally a developer can derive a class from it if you forget to mark it as sealed and finally there is no protection against instance method. The main advantages of static class are

  • Static classes only contain static members.
  • Static classes cannot be instantiated.
  • Static classes are sealed.
  • Static classes cannot contain a constructor.

The following example demonstrates static class

public static class MyStaticClass
{
public static void DispMessage(string strMessage)
{
Console.WriteLine(strMessage);
}
}
partial
class Class1
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
MyStaticClass.DispMessage("This is a static class");
}
}

Finally I conclude this discussion with one good question is when to use static class

The answer is very simple if the class's methods do not need to be attached to a specific instance of the class so instead of creating unnecessary instances of this class , declare it as static class for example if you have a class called Company contains only Two methods called GetCompanyName() and GetCompanyAddress() these methods do not need to be attach to a specific instance of any class so you can make this a static class and call where ever you need company information instead of making unnecessary instance of the class .

8. Fixed Size Buffer

Fixed size buffer provides flexibility to create fixed size data structure useful when you are working with unmanaged world such as DLLs or COM components. For example its impossible to declare C++ style fixed-size structure because C# struct containing an array doesn't contain the array elements, but instead contains a reference to the elements but in C# 2.0 provides flexibility to embed an array of fixed size in a struct when used in an unsafe code block as below

Public struct Employee
unsafe block
{
public fixed char EmpName[50];
public int EmpID;
}

The first element is a 50 bytes array and EmpID is a 4 byte int. The main advantages of the above code is EmpName array is of is fixed size and location and can be used with unsafe code.

If we want to implement the above in C# 1.X it will be as below

Public struct Employee
{
public char EmpName[];
public int EmpID;
}

The different between the above code and C# 2.0 fixed size buffer is in the above code struct would be 8 bytes in size where the EmpName array is a reference to the heap-allocated array. The other difference between unsafe buffer array and normal array is

You can only use unsafe buffers in an unsafe context

Unsafe buffers are always one-dimensional arrays

The declaration of the array should include a count, for example, char Name[15]. You cannot use char Name[] instead.

Unsafe buffers can only be instance fields of structs in an unsafe context.

The C# compiler and the CLR do not perform any security buffer overrun checks. As with all unsafe code.

9. Friend Assemblies

The friend assemblies is one of the cool stuff of C# 2.0 .The philosophy behind friend assemblies is all the non-public types in an assembly can be accessed by another assembly by declaring the first assembly is declared as a "friend" of the second for example you have some types in your assembly A and you have another assembly B. You really want assembly B to see those types. But you don't want them to open to the world. In order to implement in C# 2.0 we have to make assembly B as friend of assembly A. A friend assembly is declared by using the InternalsVisibleToAttribute as below.

[assembly:InternalsVisibleTo ("AssemblyB", PublicKeyToken="32ab4ba45e0a69a1")]

In C# 1.X you can implement by mark those types as public and then decorate them with StrongNameIdentityPermission

The main difference between StrongNameIdentityPermission and friend assembly are

  1. StrongNameIdentityPermission applies to individual type, while friend assembly applies to the whole assembly example If there are hundreds of types in assembly A that you want to share with assembly B, you have to decorate all of them with StrongNameIdentityPermission. While using friend assembly, you only need to state it once.
  2. More importantly, with StrongNameIdentityPermission, the types you want to share have to be declared as public. While in friend assembly case, those types can (and should) be non public.
  3. There can be only one StrongNameIdentityPermission on one type. But you can declare as many as assemblies as your friends.

Finally we must keep two things in mind while working with friend assemblies is Friend Assemblies is one way only that means When assembly A says assembly B is its friend, it does not imply assembly A is assembly B's friend. You will have to declare the friendship explicitly in assembly B and Friendship is not transitive that means If assembly C is friend of assembly B, and assembly B is friend of assembly A, assembly C does not automatically becomes friend of assembly A.

10. #Pragma warning

C# language has many preprocessor directives like #if,#else,#endif similarly #pragma warning is one type of preprocessor directive .these directives are used to aid in conditional compilation. Unlike C and C++ directives, you cannot use these directives to create macros and preprocessor directive must be the only instruction on a line.

The #Pragma warning directive is used to enable or restore all or certain warning message during compilation .The syntax is

#pragma warning disable warning-list (disable directive disables the specified set of warnings)
#pragma warning restore warning-list (restore directives restore the specified set of warnings)

If you disable warning externally ( I mean by setting /nowarn option) # pragma restore will not enable the warning .

For example disable and restore the CLSCompliant attribute (cs3021)

// pragma_warning.cs
using System;
#pragma warning disable 3021
[CLSCompliant(
false)]
public class MyClass
{
int i = 1;
public static void Main() // write your code here
{
}
}
#pragma warning restore 3021
[CLSCompliant(
false)]
public class MyClass
{
int i = 1;
public static void Main() // write your code here
{
}
}

Up Next
    Ebook Download
    View all

    FileInfo in C#

    Read by 9.7k people
    Download Now!
    Learn
    View all