#1,032 – Requiring Generic Type Parameters to Be a Reference or Value Type

By default, when you specify a type parameter in a generic class, the type can be any .NET type.  There are times, however, when you’d like to constrain the type in some ways, for example requiring that the type be either a reference type or a value type.

To require a type argument to represent a reference type, use the class keyword:

    public class PileOf<T> where T : class

To require a type argument to represent a non-nullable value type, use the struct keyword:

    public class PileOf<T> where T : struct

You may want to use one of these constraints when your generic type includes code that will only work on a reference type or on a value type.

Advertisement

#1,031 – Requiring Generic Type Parameters to Implement an Interface

By default, when you specify a type parameter in a generic class, the type can be any .NET type.  There are times, however, when you’d like to constrain the type in some ways, allowing only types that implement a particular interface.

You can constrain a type parameter to be of a type that implements a particular interface, using the where keyword.  In the example below, the PileOf class has a type parameter that must be a type that implements the IBark interface.  (The EverybodyBark method calls a method in IBark).

    public class PileOf<T> where T : IBark
    {
        private List<T> thePile;

        public PileOf()
        {
            thePile = new List<T>();
        }

        public void AddThing(T thing)
        {
            thePile.Add(thing);
        }

        public void EverybodyBark()
        {
            foreach (T creature in thePile)
            {
                creature.Bark();
            }
        }
    }

We can now create a PileOf<Dog>, because Dog implements IBark, but we can’t create a PileOf<Cow>.

#1,030 – Requiring Generic Type Parameters to Derive from a Specified Class

By default, when you specify a type parameter in a generic class, the type can be any .NET type.  There are times, however, when you’d like to constrain the type in some ways, allowing only certain types.

You can constrain a type parameter to be of a type that derives from (or is identical to) a particular type, using the where keyword.  In the example below, the PileOf class has a type parameter that must derive from Animal–because we look for a Habitat property.

    public class PileOf<T> where T : Animal
    {
        private List<T> thePile;
        private List<string> habitats;

        public PileOf()
        {
            thePile = new List<T>();
            habitats = new List<string>();
        }

        public void AddThing(T thing)
        {
            thePile.Add(thing);
            habitats.Add(thing.Habitat);
        }
    }

We can construct a PileOf<Dog>, but not a PileOf<int>.

            // Works
            PileOf<Dog> dogPile = new PileOf<Dog>();
            dogPile.AddThing(new Dog("Fido"));

            // Compile-time error: int can't be converted to Animal
            PileOf<int> intPile = new PileOf<int>();

#548 – No More Than One Class for a Type Parameter Constraint

When specifying constraints for a type parameter in a generic class, you can specify at most one class as a constraint, but  you can specify one or more interfaces.  If one of the constraints is a class type, it must come first.

// Type must be castable to DogToy and must implement
//   IEdible and IBuryable
public class Dog<TFavToy>
    where TFavToy : DogToy, IEdible, IBuryable