Labels

Monday, October 6, 2008

C# 2.0 - Generic Classes

Hi,

This blog post summarizes the new features of C# 2.0.

Below are the new features been introduced.

§ Partial Classes

§ Static Classes

§ Generic Classes

§ Anonymous methods

§ Iterators

§ Nullable Types

Different with Templates in C++

- With C++ templates the source code of the template is required when a template is instantiated with a specific type.

- Contrary to C++ templates, generics are not only a construct of the C# language; generics are defined with the CLR. This makes it possible to instantiate generics with a specific type in Visual Basic even though the generic class was defined with C#.

In other words...

- A generic class can be defined once and can be instantiated with many different types.

- You needn't access the source code as is necessary with C++ templates.

Advantages –

Performance

No Boxing

Type Safety

- This will enable the collection to store the objects of same type.

- Hence avoids the need of type checking while retreiving.

Binary Code Reuse

- Generics allow better binary code reuse.

- A generic class can be defined once and can be instantiated with many different types.

- You needn't access the source code as is necessary with C++ templates.

- Generic types can be defined in one language and used from any other .NET language.

This is been discussed more below - Generic Type IL Generation

Code Bloat

How much code is created with generics when instantiating them with different specific types?

- Because a generic class definition goes into the assembly, instantiating generic classes with specific types doesn't duplicate these classes in the IL code.

- However, when the generic classes are compiled by the JIT compiler to native code, a new class for every specific value type is created. This does not occur for Reference Types.

Reason being –

- Reference types share all the same implementation of the same native class. This is because with reference types only a 4-byte memory address (with 32-bit systems) is needed within the generic instantiated class to reference a reference type.

Value types are contained within the memory of the generic instantiated class, and because every value type can have different memory requirements, a new class for every value type is instantiated.

Generic Classes in Framework –

Generic Class/Interface

Non-Generic Equivalent

Collection<T>, ICollection<T>

CollectionBase, ICollection

Comparer<T>, IComparer<T>, IComparable<T>

Comparer, IComparer, IComparable

Dictionary<K, V>, IDictionary<K,V>

Hashtable, IDictionary

Dictionary<K, V>.KeyCollection

None.

Dictionary<K, V>.ValueCollection

None.

IEnumerable<T>, IEnumerator<T>

IEnumerable, IEnumerator

KeyedCollection<T, U>

KeyedCollection

LinkedList<T>

None.

LinkedListNode<T>

None.

List<T>, IList<T>

ArrayList, IList

Queue<T>

Queue

ReadOnlyCollection<T>

ReadOnlyCollectionBase

SortedDictionary<K, V>

SortedList

Stack<T>

Stack

E.g - Using the List<T> Class

public class Racer

{

public string name;

public string car;

public Racer(string name, string car)

{

this.name = name;

this.car = car;

}

}

List<Racer> racers = new List<Racer>();
racers.Add(new Racer("Michael Schumacher", "Ferrari"));
racers.Add(new Racer("Juan Pablo Montoya", "McLaren-Mercedes"));
racers.Add(new Racer("Kimi Raikkonen", "McLaren-Mercedes"));
racers.Add(new Racer("Mark Webber", "Williams-BMW"));
racers.Add(new Racer("Rubens Barichello", "Ferrari"));
 
foreach (Racer r in racers)
{
Console.WriteLine(r);
}

Custom Generic Classes

- Generics is a term used to describe generic types and are simply placeholders for actual types.

- Generics are defined with left and right brackets: <T>. The T in <T> is simply a name that describes the type that is going to be used.

- T is the standard name used to define a generic type in most of Microsoft's documentation.

- You can create generic classes, delegates, interfaces and methods.

// Generic Class:

public class MyGenericClass {}

// Generic Method:

public void MyGenericMethod <T>(T type) {}

// Generic Interface:

public interface MyGenericInterface {}

// Generic Delegate:

public delegate void MyGenericDelegate(T type);

- Code Example - Generic classes

public class Stack<T>

{

T[] items;

int count;

public void Push(T item) {...}

public T Pop() {...}

}

Stack<int> stack = new Stack<int>();

stack.Push(3);

int x = stack.Pop();

public class Dictionary<K,V>

{

public void Add(K key, V value) {...}

public V this[K key] {...}

}

Dictionary<string,Customer> dict = new Dictionary<string,Customer>();

dict.Add("Peter", new Customer());

Customer c = dict["Peter"];

Generic type declarations may have any number of type parameters.

The previous Stack<T> example has only one type parameter, but a generic Dictionary class might have two type parameters, one for the type of the keys and one for the type of the values.

Generic Type IL Generation -

- Similar to a nongeneric type, the compiled representation of a generic type is Intermediate Language (IL) instructions and metadata.

- The first time an application creates an instance of a constructed generic type, such as Stack<int>, the JIT 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 CLR creates a separate/specialized copy of the native code for each generic type instantiation with a value type, but it 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).

Generic Type Constraints

Generic Methods

- In some cases, a type parameter is not needed for an entire class but is needed only inside a particular method.

- See below examples.

With a generic method the generic type is defined with the method declaration.

void Swap<T>(ref T x, ref T y)

{

T temp;

temp = x;

x = y;

y = temp;

}

A generic method can be invoked by assigning the generic type with the method call.

int i = 4;

int j = 5;

Swap<int>(ref i, ref j);

However, because the C# compiler can get the type of the parameters by calling the Swap method, it is not required to assign the generic type with the method call. The generic method can be invoked as simply as non-generic methods.

int i = 4;

int j = 5;

Swap(ref i, ref j);

Generic classes, generic types can be restricted with the where clause.

See the swap example that accepts any type that implements the interface IAccount.

void Swap<T>(ref T x, ref T y) where T : IDto

{

T temp;

temp = x;

x = y;

y = temp;

}

Live Example

[Serializable]

public class DeepCopy

{

public static string CompanyName = "My Company";

public int Age;

public string EmployeeName;

public CopyObject Salary;

public static T MakeDeepCopy<T>(T item)

{

BinaryFormatter formatter = new BinaryFormatter();

MemoryStream stream = new MemoryStream();

formatter.Serialize(stream, item);

stream.Seek(0, SeekOrigin.Begin);

T result = (T)formatter.Deserialize(stream);

stream.Close();

return result;

}

public DeepCopy MakeDeepCopy(DeepCopy inputcls)

{

MemoryStream m = new MemoryStream();

BinaryFormatter b = new BinaryFormatter();

b.Serialize(m, inputcls);

m.Position = 0;

return (DeepCopy)b.Deserialize(m);

}

}

Call –

DeepCopy m1 = new DeepCopy();

m1.Age = 25;

m1.EmployeeName = "Ahmed Eid";

m1.Salary = new CopyObject(3000);;

DeepCopy m2 = DeepCopy.MakeDeepCopy<DeepCopy>(m1);

Generic Delegates

public delegate TSummary Action<TInput, TSummary>(TInput t, TSummary u);

public static TSummary Accumulate<TInput, TOutput>(IEnumerable<T> coll, Action<TInput, TSummary> action)

{

TSummary sum = default(TSummary);

foreach (TInput input in coll)

{

sum = action(input, sum);

}

return sum;

}

The method Accumulate can be invoked using an anonymous method as below.

decimal amount = Accumulate<Account, decimal>(accounts, delegate(Account a, decimal d) { return a.Balance + d; });

s

Hope this helps

Thanks & Regards,

Arun Manglick || Senior Tech

No comments:

Post a Comment