News & UpdatesProgrammingWeb programming StoreMy Projects
Links
Affiliates

C# Tutorial – 29 – Generics

August 26th, 2009

Generics refer to the use of type parameters, which provide a way to design code templates that can operate with different data types. Specifically, it is possible to create generic methods, classes, interfaces, delegates and events.

Generic methods

In the example below, there is a method that swaps two integer arguments.

static void Swap(ref int a, ref int b)
{
  int temp = a;
  a = b;
  b = temp;
}

To make this into a generic method that can work with any data type, a type parameter first needs to be added after the method’s name, enclosed between angle-brackets. The naming convention for type parameters is that they should start with a capital T, and then have each word that describes the parameter initially capitalized. In cases such as this however, where a descriptive name would not add much value, it is common to simply name the parameter with a capital T.

static void Swap<T>(ref int a, ref int b)
{
  int temp = a;
  a = b;
  b = temp;
}

The type parameter can now be used as any other type inside the method, and so the second thing that needs to be done to complete the generic method is to replace the data type that will be made generic with the type parameter.

static void Swap<T>(ref T a, ref T b)
{
  T temp = a;
  a = b;
  b = temp;
}

Calling generic methods

The generic method is now finished. To call it, the desired type argument needs to be specified in angle-brackets before the method arguments.

int a = 0, b = 1;
Swap<int>(ref a, ref b);

In this case, the generic method may also be called as if it was a regular method, without specifying the type argument. This is because the compiler can automatically determine the type since the generic method’s parameters use the type parameter. However, if this was not the case, or to use another type argument than the one the compiler would select, the type argument would then need to be explicitly specified.

Swap(ref a, ref b);

Whenever a generic is called for the first time during run-time, a specialized version of the generic will be instantiated that has every occurrence of the type parameter substituted with the specified type argument. It is this generated method that will be called and not the generic method itself. Calling the generic method again with the same type argument will reuse this instantiated method.

Swap<int>(ref a, ref b); // create & call Swap<int>
Swap<int>(ref a, ref b); // call Swap<int>

When the generic method is called with a new type, another specialized method will be instantiated.

long c = 0, d = 1;
Swap<long>(ref c, ref d); // create & call Swap<long>

Generic type parameters

A generic can be defined to accept more than one type parameter just by adding more of them between the angle brackets. Generic methods can also be overloaded based on the number of type parameters that they define.

static void Dummy<T, U>() {}
static void Dummy<T>() {}

Default value

When using generics, one issue that may arise is how to assign a default value to a type parameter since this value depends on the type. The solution is to use the default keyword followed by the type parameter enclosed in parentheses. This expression will return the default value no matter which type parameter is used.

static void Reset<T>(ref T a) 
{
  a = default(T); 
}

Generic classes

Generic classes allow class members to use type parameters. They are defined in the same way as generic methods, by adding a type parameter after the class name.

class Point<T>
{
  public T x, y;
}

To instantiate an object from the generic class the standard notation is used, but with the type argument specified after both class names. Note that in contrast to generic methods, a generic class must always be instantiated with the type argument explicitly specified.

Point<short> p = new Point<short>();

Generic class inheritance

Inheritance works slightly differently with generic classes. A generic class can first of all inherit from a non-generic class, also called a concrete class. Second, it can inherit from another generic class that has its type argument specified, a so called closed constructed base class. Finally, it can inherit from an open constructed base class, which is a generic class that has its type argument left unspecified.

class BaseConcrete  {}
class BaseGeneric<T>{}
 
class Gen1<T> : BaseConcrete    {} // concrete
class Gen2<T> : BaseGeneric<int>{} // closed constructed
class Gen3<T> : BaseGeneric<T>  {} // open constructed

A generic class that inherits from an open constructed base class must define all of the base class’s type arguments, even if the derived generic class does not need them. This is because only the child class’s type arguments can be sent along when the child class is instantiated.

class BaseMultiple<T, U, V> {}
class Gen4<T, U> : BaseMultiple<T, U, int> {}

This also means that a non-generic class can only inherit from a closed constructed base class, and not from an open one, because a non-generic class cannot specify any type arguments when it is instantiated.

class Con1 : BaseGeneric<int> {} // ok
class Con2 : BaseGeneric<T> {}   // error

Generic interfaces

Interfaces that are declared with type parameters become generic interfaces. Generic interfaces have the same two purposes as regular interfaces. They are either created to expose members of a class that will be used by other classes, or to force a class to implement a specific functionality. When a generic interface is implemented by a non-generic class, the type argument must be specified. The generic interface can also be implemented by a generic class, in which case the type argument can be left unspecified.

// Generic functionality interface
interface IGenericCollection<T> 
{ 
  void Store(T t); 
}
 
// Non-generic class implementing generic interface
class Box : IGenericCollection<int> 
{
  public int myBox;
  public void Store(int i) { myBox = i; }
}
 
// Generic class implementing generic interface
class GenericBox<T> : IGenericCollection<T>
{
  public T myBox;
  public void Store(T t) { myBox = t; }
}

Generic delegates

A delegate can be defined with type parameters. As an example, the generic delegate below uses its type parameter to specify the referable method’s parameter. From this delegate type a delegate object can be created that can refer to any void method that takes a single argument, regardless of its type.

class MyClass
{
  public delegate void MyDelegate<T>(T arg);
 
  public void Print(string s) 
  { 
    System.Console.Write(s); 
  }
 
  static void Main()
  {
    MyDelegate<string> d = Print;
  }
}

Generic events

Generic delegates can be used to define generic events. For example, instead of using the typical design pattern where the sender of the event is of the Object type, a type parameter can allow the senders actual type to be specified. This will make the argument strongly-typed, which allows the compiler to enforce that the correct type is used for that argument.

class MyClass
{
  delegate void MyDelegate<T, U>(T sender, U eventArgs);
  event MyDelegate<MyClass, System.EventArgs> myEvent;
}

Generics and Object

In general, using the Object type as a universal container should be avoided. The reason why Object containers, such as the ArrayList, exist in the .NET class library is because generics were not introduced until C# 2.0. When compared with the Object type, generics not only ensure type safety at compile-time, but they also remove the performance overhead associated with boxing and unboxing value types into an Object container.

// Object container class
class MyBox { public object o; }
 
// Generic container class
class MyBox<T> { public T o; }
 
class MyClass
{
  static void Main()
  {
    // .NET object container
    System.Collections.ArrayList a;  
 
    // .NET generic container (preferred)
    System.Collections.Generic.List<int> b;
  }
}

Constraints

When defining a generic class or method, compile-time enforced restrictions can be applied on the kinds of type arguments that may be used when the class or method is instantiated. These restrictions are called constraints and are specified using the where keyword. All in all there are six kinds of constraints.

First, the type parameter can be restricted to value types by using the struct keyword.

class C<T> where T : struct {} // value type

Second, the parameter can be constrained to reference types by using the class keyword.

class D<T> where T : class {} // reference type

Third, the constraint can be a class name. This will restrict the type to either that class or one of its derived classes.

class B {}
class E<T> where T : B {} // be/derive from base class

Fourth, the type can be constrained to either be or derive from another type parameter.

class F<T, U> where T : U {} // be/derive from U

The fifth constraint is to specify an interface. This will restrict the type parameter to only those types that implement the specified interface, or that is of the interface type itself.

interface I {}
class G<T> where T : I {} // be/implement interface

Finally, the type argument can be constrained to only those types that have a public parameterless constructor.

class H<T> where T : new() {} // no parameter constructor

Multiple constraints

Multiple constraints can be applied to a type parameter by specifying them in a comma separated list. Furthermore, to constrain more than one type parameter additional where clauses can be added. Note that if either the class or the struct constraint is used it must appear first in the list. Moreover, if the parameterless constructor constraint is used it must be the last one in the list.

class J<T, U>
  where T : class, I
  where U : I, new() {}

Why to use constraints

Aside from restricting the use of a generic method or class to only certain parameter types, another reason for applying constraints is to increase the number of allowed operations and method calls supported by the constraining type. An unconstrained type may only use the System.Object methods. However, by applying a base class constraint, the accessible members of that base class also become available.

class Person
{
  public string name;
}
 
class PersonNameBox<T> where T : Person
{
  public string box;
 
  public void StorePersonName(T a)
  {
    box = a.name;
  }
}

Another example below uses the parameterless constructor constraint. This constraint enables new objects of the type parameter to be instantiated.

class MyClass<T> where T : new() {}

Note that if a class has a constraint on its type parameter, and a child of that class has a type parameter which is constrained by the base class, that constraint must also be applied to the child class’s type parameter.

class MyChild<T> : MyClass<T> where T : MyClass<T>, new() {}

C# Tutorial – 28 – Events

August 26th, 2009

Events enable an object to notify other objects when something of interest occurs. The object that raises the event is called the publisher and the objects that handle the event are called subscribers.

Publisher

To demonstrate the use of events the publisher will be created first. This will be a class that inherits from ArrayList, but this version will raise an event whenever an item is added to the list. Before the event can be created a delegate is needed that will hold the subscribers. This could be any kind of delegate, but the standard design pattern is to use a void delegate that accepts two parameters. The first parameter specifies the source object of the event, and the second parameter is a type that either is or inherits from the System.EventArgs class. This parameter usually contains the details of the event, but in this example there is no need to pass any event data and so the base EventArgs class will be used as the parameter’s type.

public delegate void EventHandlerDelegate(object sender, System.EventArgs e);
 
class Publisher : System.Collections.ArrayList
{
  // …
}

Event keyword

With the delegate defined, the event can be created in the Publisher class using the event keyword followed by the delegate and the name of the event. The event keyword creates a special kind of delegate that can only be invoked from within the class where it is declared. Its access level is public so that other classes are allowed to subscribe to this event. The delegate that follows the event keyword is called the event delegate. The name of the event is commonly a verb. In this case the event will be raised after the item has been added so the past-tense of the verb “Add” is used, which is “Added”. If a pre-event was created instead, which is raised before the actual event, then the –ing form of the verb would be used, in this case “Adding”.

public event EventHandlerDelegate Added;

Alternatively, in place of this custom event delegate the predefined System.EventHandler delegate could have been used. This delegate is identical to the one defined previously, and is used in the .NET class libraries for creating events that have no event data.

Event caller

To invoke the event an event caller can be created. The naming convention for this method is to precede the event’s name with the word “On”, which in this case becomes “OnAdded”. The method has the protected access level to prevent it from being called from an unrelated class, and it is marked as virtual to allow deriving classes to override it. It takes the event arguments as its one parameter, which in this case is of the EventArgs type. The method will only raise the event if it is not null, meaning only when the event has any registered subscribers. To raise the event the this instance reference is passed as the sender, and the EventArgs object is the object that was passed to the method.

protected virtual void OnAdded(System.EventArgs e)
{
  if (Added != null) Added(this, e);
}

Raising events

Now that the class has an event and a method for calling it, the final step is to override the ArrayList‘s Add method to make it raise the event. In this overridden version of the method the base class’s Add method is first called, and the result is stored. The event is then raised with the OnAdded method, by passing to it the Empty field in the System.EventArgs class, which represents an event with no data. Finally, the result is returned to the caller.

public override int Add(object value)
{
  int i = base.Add(value);
  OnAdded(System.EventArgs.Empty);
  return i;
}

The complete publisher class now has the following appearance.

class Publisher : System.Collections.ArrayList
{
  public delegate void EventHandlerDelegate(object sender, System.EventArgs e);
  public event EventHandlerDelegate Added;
 
  protected virtual void OnAdded(System.EventArgs e)
  {
    if (Added != null) Added(this, e);
  }
 
  public override int Add(object value)
  {
    int i = base.Add(value);
    OnAdded(System.EventArgs.Empty);
    return i;
  }
}

Subscriber

To make use of the publisher class another class will be created that will subscribe to the event.

class Subscriber
{
  // …
}

Event handler

This class contains an event handler, which is a method that has the same signature as the event delegate and is used to handle an event. The name of the handler is commonly the same as the name of the event followed by the “EventHandler” suffix.

class Subscriber
{
  public void AddedEventHandler(object sender, System.EventArgs e)
  { 
    System.Console.WriteLine("AddEvent occurred");  
  }
}

Subscribing to events

The publisher and subscriber classes are now complete. To demonstrate their use, a Main method is added where objects of the Publisher and Subscriber classes are created. In order to register the handler in the Subscriber object to the event in the Publisher object, the event handler is added to the event as if it was a delegate. Unlike a delegate, however, the event may not be called directly from outside its containing class. Instead, the event can only be raised by the Publisher class, which in this case occurs when an item is added to that object.

class MyApp
{
  static void Main()
  {
    Subscriber s = new Subscriber();
    Publisher  p = new Publisher();
 
    p.Added += s.AddedEventHandler;
    p.Add(10); // AddEvent occurred
  }
}

C# Tutorial – 27 – Delegates

August 26th, 2009

A delegate is a type used to reference a method. This allows methods to be assigned to variables and passed as arguments. The delegate’s declaration specifies the method signature to which objects of the delegate type can refer. Delegates are by convention named with each word initially capitalized followed by “Delegate” at the end of the name.

delegate void MyDelegate(string s);

A method that matches the delegate’s signature can be assigned to a delegate object of this type.

class MyApp
{
  static void Print(string t) { System.Console.Write(t); }
 
  static void Main()
  {
    MyDelegate d = Print;
  }
}

This delegate object will behave as if it was the method itself, no matter whether it refers to a static or an instance method. A method call on the object will be forwarded by the delegate to the method, and any return value will be passed back through the delegate.

MyDelegate d = Print;
d("Hello"); // "Hello"

The syntax used above to instantiate the delegate is actually a simplified notation that was introduced in C# 2.0. The backwards compatible way to instantiate a delegate is to use the regular reference type initialization syntax.

MyDelegate d = new MyDelegate(Print);

Anonymous methods

C# 2.0 also introduced anonymous methods, which can be assigned to delegate objects. An anonymous method is specified by using the delegate keyword followed by a method parameter list and body. This can simplify the delegate’s instantiation since a separate method will not have to be defined in order to instantiate the delegate.

MyDelegate f = delegate(string t) { System.Console.Write(t); };

Lambda expressions

C# 3.0 went one step further and introduced lambda expressions. They achieve the same goal as anonymous methods, but with a more concise syntax. A lambda expression is written as a parameter list followed by the lambda operator (=>) and an expression.

delegate int MyDelegate(int i);
static void Main()
{
  // Anonymous method
  MyDelegate a = delegate(int x) { return x * x; };
 
  // Lambda expression
  MyDelegate b = (int x) => x * x;
 
  a(5); // 25
  b(5); // 25
}

The lambda must match the signature of the delegate. Typically, the compiler can determine the data type of the parameters from the context, so they do not need to be specified. The parentheses may also be left out if the lambda has only one input parameter.

MyDelegate c = x => x * x;

If no input parameters are needed an empty set of parentheses must be specified.

delegate void MyEmptyDelegate();
// …
MyEmptyDelegate d = () => System.Console.Write("Hello");

A lambda expression that only executes a single statement is called an expression lambda. The expression of a lambda can also be enclosed in curly brackets to allow it to contain multiple statements. This form is called a statement lambda.

MyDelegate e = (int x) => { 
  int y = x * x;
  return y;
};

Multicast delegates

It is possible for a delegate object to refer to more than one method. Such an object is known as a multicast delegate and the methods it refers to are contained in a so called invocation list. To add another method to the delegate’s invocation list, either the addition operator or the addition assignment operator can be used.

static void Hi()  { System.Console.Write("Hi"); }
static void Bye() { System.Console.Write("Bye"); }
// …
MyDelegate d = Hi;
d = d + Hi;
d += Bye;

Similarly, to remove a method from the invocation list, the subtraction or subtraction assignment operators are used.

d -= Hi;

When calling a multicast delegate object, all methods in its invocation list will be invoked with the same arguments in the order that they were added to the list.

d(); // "HiBye"

If the delegate returns a value, only the value of the last invoked method will be returned. Likewise, if the delegate has an out parameter, its final value will be the value assigned by the last method.

Delegate signature

As mentioned before, a method can be assigned to a delegate object if it matches the delegate’s signature. However, a method does not have to match the signature exactly. A delegate object can also refer to a method that has a more derived return type than that defined in the delegate, or that has parameter types that are ancestors of the corresponding delegate’s parameter types.

class Base {}
class Derived : Base {}
 
delegate Base MyDelegate(Derived d);
 
class MyApp
{
  static Derived Dummy(Base o) { return new Derived(); }
 
  static void Main()
  {
    MyDelegate d = Dummy;
  }
}

Delegates as parameters

An important property of delegates is that they can be passed as method parameters. To demonstrate the benefit of this, two simple classes will be defined. The first one is a data storage class called PersonDB that has an array containing a couple of names. It also has a method that takes a delegate object as its argument, and calls that delegate for each name in the array.

delegate void ProcessPersonDelegate(string name);
 
class PersonDB
{
  string[] list = { "John", "Sam", "Dave" };
 
  public void Process(ProcessPersonDelegate f)
  { foreach (string s in list) f(s); }
}

The second class is Client, which will use the storage class. It has a Main method that creates an instance of PersonDB, and it calls that object’s Process method with a method that is defined in the Client class.

class Client
{
  static void Main()
  {
    PersonDB p = new PersonDB();
    p.Process(PrintName);
  }
 
  static void PrintName(string name)
  { 
    System.Console.WriteLine(name); 
  }
}

The benefit of this approach is that it allows the implementation of the data storage to be separated from the implementation of the data processing. The storage class only handles the storage and has no knowledge of the processing that is done on the data. This allows the storage class to be written in a more general way than if this class had to implement all of the potential processing operations that a client may want to perform on the data. With this solution, the client can simply plug its own processing code into the existing storage class.

C# Tutorial – 26 – Preprocessor

August 26th, 2009

C# includes a set of preprocessor directives that are mainly used for conditional compilation. Although the C# compiler does not have a separate preprocessor, as C and C++ compilers, the directives shown below are processed as if there was one. That is, they appear to be processed before the actual compilation takes place.

DirectiveDescription
#if
#elif
#else
#endif
If
Else if
Else
End if
#define
#undef
Symbol define
Symbol undefine
#error
#warning
#line
Generate error
Generate warning
Set line number
#region
#endregion
Mark section start
Mark section end

Preprocessor directive syntax

The preprocessor directives are easily distinguished from normal programming code in that they start with a hash sign (#). They must always occupy a line that is separate from anything else, except for single-line comments. Whitespace may optionally be included before and after the hash mark.

#line 1 // set line number

Conditional compilation – #if and #endif

The #if and #endif directives specify a section of code that will be included or excluded based on a given condition. Most often, this condition will be a conditional compilation symbol.

#if MySymbol
 // …
#endif

Defining symbols

A conditional compilation symbol is created using the #define directive followed by the symbol’s name. When a symbol is defined, it will then cause a conditional expression using that condition to be evaluated as true. The symbol will remain defined only within the current source file starting from the line where the symbol is created.

#define MySymbol

Undefining symbols

The #undef (undefine) directive can disable a previously defined symbol.

#undef MySymbol

Conditional compilation – #elif and #else

Just as with the C# if statement, the #if directive can optionally include any number of #elif (else if) directives and one final #else directive. Conditional directives may also be nested within another conditional section. In longer conditionals, it is a good practice to add comments to the #endif directives to help keep track of which #if directive they correspond to.

#if Professional
 // …
#elif Advanced || Enterprise
 // …
#else
  #if Debug
   // …
  #endif // Debug
#endif // not Professional, Advanced or Enterprise

Diagnostic directives

There are two diagnostic directives: #error and #warning. The #error directive is used to abort a compilation by generating a compilation error. This directive can optionally take a parameter that specifies the error description.

#if Professional && Enterprise
 #error Build cannot be both Professional and Enterprise
#endif

Similar to error, the #warning directive generates a compilation warning message. This directive will not stop the compilation.

#if !Professional && !Enterprise
 #warning Build should be Professional or Enterprise
#endif

Line directive

Another directive that affects the compiler’s output is #line. This directive is used to change the line number and optionally the source file name that is displayed when an error or warning occurs during compilation. This is mainly useful when using a program that combines the source files into an intermediate file which is then compiled.

#line 500 "MyFile"
#error MyError // MyError on line 500

Region directive

The last two directives are #region and #endregion. They delimit a section of code that can be expanded or collapsed using the outlining feature of Visual Studio.

#region MyRegion
#endregion

Just as the conditional directives, regions can be nested any number of levels deep.

#region MyRegion
 #region MySubRegion
 #endregion
#endregion

C# Tutorial – 25 – Constants

August 26th, 2009

A variable in C# can be made into a compile-time constant by adding the const keyword before the data type. This modifier means that the variable cannot be changed and it must therefore be assigned a value at the same time as it is declared. Any attempts to assign a new value to the constant will result in a compile-time error.

Local constants

A local constant must always be initialized at the same time as it is declared.

static void Main()
{
  const int a = 10; // compile-time constant
}

The const modifier creates a compile-time constant, and so the compiler will replace all usage of the constant with its value. The assigned value must therefore be known at compile-time. As a result of this, the const modifier may only be used together with the simple types, as well as with enum and string types.

Constant fields

The const modifier can be used on fields as well as on local variables. Unlike C++, C# does not allow method parameters to be made constant.

class MyClass
{
  const int b = 5; // compile-time constant field
}

A field that is marked with const is accessed as if it was a static field. Constant fields cannot be made static.

int c = MyClass.b;

Readonly keyword

Another variable modifier similar to const is readonly, which creates a run-time constant. This modifier may only be applied to fields, and like const it makes the field unchangeable.

class MyClass
{
  readonly int d = 3; // run-time constant field
}

However, since a readonly field is assigned at run-time it can be assigned a dynamic value that is not known until run-time.

readonly int e = System.DateTime.Now.Hour;

Unlike const, readonly can be applied to any data type.

readonly int[] f = { 1, 2, 3 };

In addition, a readonly field cannot only be initialized when it is declared. It can alternatively be assigned a value in the constructor.

class MyClass
{
  readonly string s;
  public MyClass() { s = "Hello World"; }
}

Constant guideline

In general, it is a good idea to always declare variables as constants if they do not need to be reassigned. This ensures that the variables will not be changed anywhere in the program by mistake, which in turn helps to prevent bugs.

C# Tutorial – 24 – Custom Conversions

August 26th, 2009

This section covers how to define custom type conversions for an object. As can be seen in the example below, there is a class called MyNum with a single int field and a constructor. With a custom type conversion, it is possible to allow an int to be implicitly converted to an object of this class.

class MyNum
{
  public int val;
  public MyNum(int i) { val = i; }
}

Implicit conversion methods

For this to work an implicit conversion method needs to be added to the class. This method’s signature looks similar to that used for unary operator overloading. It must be declared as public static and includes the operator keyword. However, instead of an operator symbol the return type is specified, which is the target type for the conversion. The single parameter will hold the value that is to be converted. The implicit keyword is also included, which specifies that the method is used to perform implicit conversions.

public static implicit operator MyNum(int a) 
{
  return new MyNum(a);
}

With this method in place an int can be implicitly converted to a MyNum object.

MyNum a = 5;

Another conversion method can be added that handles conversions in the opposite direction, from a MyNum object to an int.

public static implicit operator int(MyNum a) 
{
  return a.val;
}

Explicit conversion methods

To prevent potentially unintended object type conversions by the compiler, the conversion method can be declared as explicit instead of implicit.

public static explicit operator int(MyNum a) 
{
  return a.val;
}

The explicit keyword means that the programmer has to specify an explicit cast in order to invoke the type conversion method. In particular, explicit conversion methods should be used if the result of the conversion leads to loss of information, or if the conversion method may throw exceptions.

MyNum a = 5;
int i = (int)a;
  1. 1
  2. ...
  3. 7
  4. 8
  5. 9
  6. 10
  7. 11
  8. ...
  9. 37
Ads