Today I've been talking to some of our developers about generics in C#.  As part of it, we worked through an example, the Badger Example, which I've reproduced here for those intrigued!

Overview

This document demonstrates the creation of a generic class called Badger. Its purpose is entirely educational and introduces the use of type parameter constraints.

The Initial Badger Class

class Badger<TBadgerCharacter>
{
    TBadgerCharacter _character;

    public Badger(TBadgerCharacter character)
    {
        // TODO: Use the new operator
        _character = character;
    }

    public void HelloBadger()
    {
        // TODO: Call the Bark method on
        //       _character
    }
}

The constructor for this class is passed an instance of a TBadgerCharacter type. The type passed should provide a Bark() method and this method should be called as part of the HelloBadger() method. Additionally, rather than receiving an instance of the TBadgerCharacter type, one should be created in the constructor.

Enabling the new operator with a default constructor

The first problem can be solved by adding a constraint on the template parameter. The following code will not compile because it does not include such a constraint:

class Badger<TBadgerCharacter>
{
    TBadgerCharacter _character;

    public Badger()
    {
        _character = new TBadgerCharacter();
    }

    public void HelloBadger()
    {
        // TODO: Call the Bark method on
        //       _character
    }
}

This code does not compile: TBadgerCharacter does not provide a default constructor

Here’s the code with the required constraint so that it will compile:

class Badger<TBadgerCharacter>
    where TBadgerCharacter : new()
{
    TBadgerCharacter _character;

    public Badger()
    {
        _character = new TBadgerCharacter();
    }

    public void HelloBadger()
    {
        // TODO: Call the Bark method on
        //       _character
    }
}

This code does compile: The new() constraint requires TBadgerCharacter to provide a default constructor

Enabling the Bark() method with a type constraint

To enable the Bark() method on TBadgerCharacter there are two requirements. Firstly, a class or interface that declares the Bark() method must be created. Secondly, a constraint must be added to require a compatible type. Here’s the first part of the solution:

interface IBadgerCharacter
{
    void Bark();
}

The first step is to create a class or interface that provides the required method. This pattern of creating an interface for a generic type parameter is very common and good practice.

The next step is to require the constraint. Here’s the code beforehand:

interface IBadgerCharacter
{
    void Bark();
}

class Badger<TBadgerCharacter>
    where TBadgerCharacter : new()
{
    TBadgerCharacter _character;

    public Badger()
    {
        _character = new TBadgerCharacter();
    }

    public void HelloBadger()
    {
        _character.Bark();
    }
}

This code does not compile: The TBadgerCharacter type does not implement the IBadgerCharacter interface or provide a Bark() method.

Here’s the code with an appropriate constraint:

interface IBadgerCharacter
{
    void Bark();
}

class Badger<TBadgerCharacter>
    where TBadgerCharacter : IBadgerCharacter, new()
{
    TBadgerCharacter _character;

    public Badger()
    {
        _character = new TBadgerCharacter();
    }

    public void HelloBadger()
    {
        _character.Bark();
    }
}

This code does compile: The TBadgerCharacter type is required to implement a default constructor and also the IBadgerCharacter interface.

A final comment about constraints

Multiple constraints can be placed on a generic template parameter. These constraints fall into the following categories:

  1. either or neither of a Value Type or Reference Type constraint
    • use the class keyword for a reference type
    • use the struct keyword for a value type
  2. zero or more base class and/or interface implementation requirements
    • for instance, the IBadgerCharacter interface in the above example
  3. optionally a default constructor constraint
    • use the new() keyword—the parentheses are required

These categories of constraint must be specified in the order shown. The order of individual classes and interfaces in the second category is not important.

Constraints on multiple generic template parameter types must be specified with completely separate where clauses.

Versions

  • Microsoft .NET Framework 2.0

Metadata


Bookmark with :
Digg It! DZone StumbleUpon Technorati Reddit Del.icio.us Newsvine Furl Blinklist
posted @ Tuesday, May 20, 2008 3:08 PM | in .NET Software Development

Comments

No comments posted yet.

Post Comment

Title *
Name *
Email
Url
Comment *  


Please add 4 and 6 and type the answer here: