2. Changepond Technologies, Confidential
GENERICS: OVERVIEW
Introduction:
o Generics allow developers to define types and methods that are parameterized with type
parameters.
o Those parameters are ultimately filled in when such types or methods are used.
2
3. Changepond Technologies, Confidential
GENERICS: OBJECTIVES
o Objective:
After completing this chapter you will be able to:
Describe Generics
Different implementation of generics
• Generic Methods
• Generic Constraints
3
4. Changepond Technologies, Confidential
GENERICS - A PRACTICAL SCENARIO
4
Suppose you’re writing an implementation of a list data structure, which maintains a
sequential ordering of elements and allows index-based access to its elements.
Our collection type provides an improvement over arrays because it allows for the
underlying storage to grow when needed.
Internally, the most obvious way to implement this is by using an array to store the
elements and copy elements to a larger array when storage space is exceeded.
class ArrayList {
private object[] _elements;
...
public object this[int i] {
get { return _elements[i]; }
set { _elements[i] = value; }
}
public void Add(object o) {
// Add to array, dynamically growing as necessary.
}
...
}
Looks Good. Any problem ?
5. Changepond Technologies, Confidential
GENERICS: PRACTICAL SCENARIO
5
ArrayList xs = new ArrayList();
xs.Add(1);
xs.Add(2);
xs.Add(3);
//Evil mind at play
xs[0] = “Should not be here”;
Excessive amount of casts
to work with the API
The compiler doesn’t catch any
violations (compile time)against any
implicit contract with regard to the (by
the human) expected types fed in to
the collection
6. Changepond Technologies, Confidential
GENERICS: PRACTICAL SCENARIO
o When creating a generic type, whether it’s a class, struct, interface, or delegate, you’re
adding one or more type parameters to the type declaration.
o On the other side , a user of the type can specify types for those parameters
6
class List<T> {
private T[] _elements;
...
public T this[int i] {
get { return _elements[i]; }
set { _elements[i] = value; }
}
public void Add(T o) {
// Add to array, dynamically
growing as necessary.
}
...
}
List<int> xs = new List<int> { 1, 2, 3 };
List<string> names = new List<string> { “Bart”, “John” };
//Lets test if the evil intentions work !!
List<int> xs = new List<int> { 1, 2, 3 };
xs[0] = “Should not be here”; //Evil statement
int first = xs[0]; // No need to cast here anymore...
Better compile
time Checking
7. Changepond Technologies, Confidential
GENERIC TYPES DECLARATION
o Declaring Generic Types
class Dictionary<TKey, TValue>
o Stylistically, generic type parameters are prefixed with a capital T, followed by a description of the
parameter.
7
class List<T> {
private T[] _elements;
}
Applied to a
class
public interface IEnumerable<out T> : IEnumerable {
IEnumerator<T> GetEnumerator();
}
Applied to a
Interfaces
Not only classes support generics. Structs , Methods and
interfaces also do
8. Changepond Technologies, Confidential
GENERIC DECLARATIONS
o Using Generic Types
8
var numbers = new Dictionary<string, int> {
{ “Bart”, 911 },
{ “John”, 119 }
};
Substituting the TKey type
parameter for string and the
TValue type parameter for int
9. Changepond Technologies, Confidential
GENERIC TYPE – ENFORCING CONSTRAINTS
o Generic Constraints
9
class Foo<T> {
public void Bar(T input) {
// What can be done with T?
}
}
class Foo<T> {
private T _field;
public void Bar(T input) {
_field = input;
}
}
May be to store the Value
in a private file
Is it possible to invoke a method in T ?
How do we define a method for T ?
Define and invoke
Methods on T using
Generic Constraints
10. Changepond Technologies, Confidential
o Consider the declaration OrderedList<T>
How do we know what methods that T offers ?
How do we make sure T is Compared so that it can be ordered?
May be we need some sort of contract that T should adhere to..
1
class OrderedList<T> where T :
IComparable<T> {
// Now we can use the CompareTo
method on objects of type T...
}
OrderedList<T> can only accept
parameter T,types that implement
IComparable<T>.
Generic Type – Enforcing
Constraints
11. Changepond Technologies, Confidential
GENERIC TYPE – ENFORCING CONSTRAINTS
1
class OrderedList<T> where T : IComparable<T> {
private List<T> _elements = new List<T>();
public void Add(T value) {
int i = 0;
while (i < _elements.Count) {
if (_elements[i].CompareTo(value) >= 0)
break;
i++;
}
_elements.Insert(i, value);
}
}
12. Changepond Technologies, Confidential
GENERICS – CREATING GENERIC METHODS
o Generics Methods
Generic methods enable you to specify a set of related methods with a single method
declaration. Like a Generic classes enable you to specify a set of related classes with a single
class declaration
Generic Method provide compile-time type safety like its other Generic types.
Generic methods provide a implicit way of method overloading.
Consider the code snippet below.
1
int[] intArray = { 1, 2, 3, 4, 5, 6 };
double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
char[] charArray = { 'H', 'E', 'L', 'L', 'O' };
Console.WriteLine( “Printing all the arrays using PrintArray overload methods:" );
PrintArray( intArray ); // Overload for printing Integer Array
PrintArray( doubleArray ); // Overload for Printing double Array
PrintArray( charArray ); // Overload for printing Char Array
}
13. Changepond Technologies, Confidential
GENERICS – CREATING GENERIC METHODS
o The above code snippet can be rewritten using a generic method of DisplayArray like the
one below.
o Call the generic method in your code like below
1
private static void PrintArray( T[] inputArray )
{
foreach ( T element in inputArray )
Console.Write( element + " " );
Console.WriteLine( "n" );
}
PrintArray <int[]> ( intArray ); // pass an int array argument
PrintArray <double[]>( doubleArray ); // pass a double array argument
PrintArray <char[]> ( charArray ); // pass a char array argument
15. Changepond Technologies, Confidential
DELEGATES
o Delegates were introduced with C# 1.0 (with .NET 1.0) as a way to represent function
pointers
o Delegates are type-safe object that can point to another method (or possibly multiple
methods) in the application, which can be invoked at later time.
o A delegate type maintains three important pieces of information :
– The name of the method on which it make calls
– Any argument (if any) of this method.
– The return value (if any) of this method.
1
16. Changepond Technologies, Confidential
DELEGATES
o Delegates in .NET 1.x Era
1
// Step 1 Declare the delegate
delegate int MyDelegate(int firstNumber, int secondNumber);
//Step 2 Declare a method that matches the delegate signature:
private int AddNumbers(int firstNumber, int secondNumber)
{
return firstNumber + secondNumber;
}
//Step 3 Instantiate the delegate (tell the delegate with method to call)
MyDelegate myDelegate = new MyDelegate(this.AddNumbers);
//Step 4Call the delegate and retrieve the result
int result = myDelegate(3, 4);
17. Changepond Technologies, Confidential
DELEGATES
o Delegates in .NET 2.0 – 3.0 using Ananymous Methods
o .NET 2.0 introduced a concept called anonymous methods. This allowed us to call inline
business logic (i.e., a method) without having to have the method defined.
o The business logic simply became part of the delegate.
1
//Step 1 Delcare the delegate:
delegate int MyDelegate(int firstNumber, int secondNumber);
//Step 2 Instantiate the delegate (with the business logic defined inside the delegate "body")
MyDelegate myDelegate= delegate(int firstNumber, int secondNumber)
{
return firstNumber + secondNumber;
};
//Step 3 Call the delegate:
int result = myDelegate(3, 4);
18. Changepond Technologies, Confidential
LAMBDA EXPRESSION WITH DELEGATES
oLambda expressions are a further evolution
of syntax brevity for anonymous methods.
oDeclare the business logic inline with
delegates
oLambda Expr are syntactic shortcut for
delegates
1
//Step 1Declare a delegate
delegate int MyDelegate(int firstNumber, int secondNumber);
//Step 2 Initialize the delegate using a lambda expressions (new in .NET 3.5)
MyDelegate myDelegate = (firstNumber, secondNumber) => firstNumber + secondNumber;
//Setp 3 : Call the delegate
int result= myDelegate(3,4);
19. Changepond Technologies, Confidential
LAMBDA EXPRESSION & FUNC DELEGATE
o The other way of using Lambda is by using the new Func delegate.
o A Func is a delegate type with several generic parameters defined.
o There are almost 16 different Func delegates pre-defined that covers the most common
scenarios
1
//For example, the Func below has 2 input parameters and a return parameter. This one of
that is pre-defined in .NET 3.5
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);
//Using the above Func delegate
Func<int, int, int> myDelegate= (firstNumber, secondNumber) => firstNumber + secondNumber;
int i=myDelegate(4,5);
20. Changepond Technologies, Confidential
LAMBDA EXPRESSION & ACTION DELEGATE
o As with Func, the Action delegate has five variations.
o These allow the encapsulation of methods that have up to 16 parameters but do not
return a value. Again, all of the parameters are generic types allowing any type to be used
for each argument.
2
public delegate void Action()
public delegate void Action<T, >(T arg)
public delegate void Action<T1, T2>(T1 arg1, T2 arg2)
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
public delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4)
…
21. Changepond Technologies, Confidential
LAMBDA EXPRESSION & ACTION DELEGATES
o The simplest Action delegate is used with methods that accept no parameters and return
no values.
2
Action showMessage = delegate { Console.WriteLine("Hello, world"); };
showMessage(); // Outputs "Hello, world"
Action showMessage = () => { Console.WriteLine("Hello, world"); };
showMessage(); // Outputs "Hello, world“
Lambda
Expr
Using Action with Parameters
Action<string> showMessage = delegate(string msg) { Console.WriteLine(msg); };
showMessage("Hello, world"); // Outputs "Hello, world"
Using Action with Parameters
Action<string> showMessage = (msg)=>{ Console.WriteLine(msg); };
showMessage("Hello, world"); // Outputs "Hello, world"
Lambda
Expr
22. Changepond Technologies, Confidential
PARTIAL CLASSES
o The partial keyword was introduced in version 2.0 of the .NET framework. Very simply, the
partial keyword permits us to split the definition of a single class, structure, or interface
into more than one file.
o Each source file contains a section of the class definition, and all parts are combined when
the application is compiled.
o More than one developer can simultaneously write the code for the class.
o We can easily write our code (for extended functionality) for a VS.NET generated or other
code generation tool generated class. This will allow us to write the code of our own need
without messing with the system generated code.
2
23. Changepond Technologies, Confidential
KEY POINTS ON PARTIAL CLASSES
o All the partial definitions must proceeded with the key word "Partial".
o All the partial types meant to be the part of same type must be defined within a same
assembly and module.
o Method signatures (return type, name of the method, and parameters) must be unique for
the aggregated typed (which was defined partially).
o The partial types must have the same accessibility.
o If any part is sealed, the entire class is sealed.
o If any part is abstract, the entire class is abstract.
o Inheritance at any partial type applies to the entire class.
2
24. Changepond Technologies, Confidential
NESTED CLASSES
o A nested class is one that is created inside another class.
o There are several reasons to have inner classes. The two important points are:
Organizing code into real world situations where there is a special relationship between two
objects.
Hiding a class within another class so that we do not want the inner class to be used from
outside of the class it is created within.
2
25. Changepond Technologies, Confidential
NESTED CLASSES
o Declaration and Use
A nested class is declared in the same manner as a normal class declaration. The difference is
that a nested class has access to all of the available modifiers.
o The this keyword reference in the inner class only holds a reference to the inner class.
Data members of the outer class are not accessible using the this reference in the inner
class. If this is needed, pass a reference of the outer class into the constructor of the inner
class.
o Static members of the outer class are available in the inner class irrespective of the
accessibility level.
2
26. Changepond Technologies, Confidential
NESTED CLASSES -AN EXAMPLE
o For example, a class named Car would have an engine. Objects that fall into this
relationship use of the term "is a part of". "An engine is a part of a car". In UML, an object
relationship that is formed by aggregation is drawn using an empty diamond. An object
relationship that is formed using composition is drawn using a filled diamond.
o The following UML diagram illustrates the concepts of aggregation and composition.
2
28. Changepond Technologies, Confidential
NESTED CLASS :LEND A HAND
o From the previous diagram, Try to create a possible small code sample that shows the two
forms of relationship.
Note: Objects that use composition should be created as inner classes.
2
29. Changepond Technologies, Confidential
LEND A HAND - ANSWERS
2
namespace MyCars
{
// Inherit from class Car
public class FordCapri : Car
{
public FordCapri()
{ theEngine.horsePower = 2000;
}
}
}
30. You have successfully completed
Generics, Delegates and Lambda Expr
Click here to proceed
Editor's Notes
In the preceding example, the evil guy can just go ahead and stuff a string in our collection:
It’s a System.Object anyway, so the compiler is fine with it. However, when getting it
out we’re still working under the assumption only integer values could have been added to
the collection, so we’re casting the result of an indexer operation back to int. The only way
for the CLR to guarantee type safety is to check types dynamically at runtime (which is the
case for casts in general) and throw an exception if a violation is detected
Defining our own generic contract (interfaces ) :
public interface IComparable<in T> {
int CompareTo(T other);
}
A delegate is an object that holds a reference to a method. Delegates allow you to treat
methods as data—via delegates, you can assign methods to variables, and pass methods to
and from other methods. You can also call methods through variables of delegate types
NET 3.5 Solution (Type 1) (C# 3.0)
.NET 3.5 (C# 3.0) introduces even more brevity to delegate syntax with lambda expressions. Lambda expressions are a further evolution of syntax brevity for anonymous methods. You take the spirit of declaring the business logic inline with delegates even further. I broke the .NET 3.5 solution into type 1 and type 2. Type 1 uses just uses the new lambda expression syntax.
This solution has several parts:
Declare the delegate with the desired signature
Instantiate the delegate (with the business logic defined inside the delegate "body")
Call the delegate and retrieve the result
Delcare the delegate:
delegate int Add(int firstNumber, int secondNumber);
The delegate type declaration is the same as the prior version(s).
Instantiate the delegate (with the business logic defined inside the delegate "body")
Add add = (firstNumber, secondNumber) => firstNumber + secondNumber;
--- That looks different. Let's take a step back and see how we got to the lambda expression from the anonymous method.
In the .NET 2.0 version we had something like this:
Add add = delegate(int firstNumber, int secondNumber)
{
return firstNumber + secondNumber;
};
First, let's remove the delegate keyword. Since the delegate keyword back in the .NET 2.0 (C# 2) version told the compiler we were declaring an anonymous method, it needs to be replaced since we want to use lamdba expressions. Since we are going to replace the anonymous method syntax with lambda expression syntax, we need to use the keyword that tells the compiler we are working a lambda expression. This keyword is "=>". The compiler would like to see this after the parameters are defined but before the inline body of the expression.
Our first step in our lambda expression will look like this (This is valid syntax and will compile):
Add add = (int firstNumber, int secondNumber) =>
{
return firstNumber + secondNumber;
};
The expression now reads: The parameters of integer firstNumber and integer secondNumber "goes to" return the sum of firstNumber plus secondNumber. The code above is now a fully functioning Lamdba Expression and not an anonymous method anymore. Replacing the "delegate" keyword with the appropriate location for the "=>" keyword converted this from an anonymous method to a lambda expression. This is very important, so the compiler with this simple keyword swap now knows we are dealing with a lambda expression.
We didn't really gain much here. We essentially replaced one keyword with another. What we can do next is add additional lambda expression shortcuts. Lambda expressions do not require curly braces or return type.
If we remove both of these, this expression now becomes:
Add add = (int firstNumber, int secondNumber) => firstNumber + secondNumber;
That's getting much better. Notice, no curly braces or the return keyword; the C# 3.0 compiler can derive all this information for us! The expression looks more concise now. But we can do better. The compiler is even smarter now since it can detect the type of parameters we want to use. We don't need to explicitly say that we are passing in two integers; this is detected by the compiler.
Now the full expression (after removing the two int keywords) remains as just the parameter names:
Add add = (firstNumber, secondNumber) => firstNumber + secondNumber;
Call the delegate and retrieve the result
int result = add(5), Convert.ToInt32(6));
The Func and Action generic delegates were introduced in the .NET framework version 3.5. They provide flexible delegates with generic parameters that can be used for many purposes, including passing lambda expressions to method parameters
Func<double> taxRate = delegate { return 17.5; }; Console.WriteLine("{0}%", taxRate()); // Outputs "17.5%"
Same above funciton with lambda expression
Func<double> taxRate = () => 17.5; Console.WriteLine("{0}%", taxRate()); // Outputs "17.5%"
Action showMessage = delegate { Console.WriteLine("Hello, world"); }; showMessage(); // Outputs "Hello, world"
As with the Func delegates, Action can be used with lambda expressions. The equivalent of the previous example can be created using a statement lambda, as follows:
Action showMessage = () => { Console.WriteLine("Hello, world"); }; showMessage(); // Outputs "Hello, world"
Using Action with Parameters
As a final example of the basic use of Action, consider the following example. This code uses a single string parameter for the delegate. The parameter accepts a message to be outputted to the console.
Action<string> showMessage = delegate(string msg) { Console.WriteLine(msg); }; showMessage("Hello, world"); // Outputs "Hello, world"
This example can be recreated using a statement lambda that includes a parameter. In this case the string parameter's type is inferred.
Action<string> showMessage = (msg) => { Console.WriteLine(msg); }; showMessage("Hello, world"); // Outputs "Hello, world"