#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>.

Advertisements

#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

#547 – Things That Can Serve as Type Parameter Constraints

A constraint on a type parameter is often a base class or interface, but can actually take on a number of different forms.

A constraint can be a class type:

    // Type parameter can be some subclass of DogToy,
    // e.g. SqueakyToy, RopeToy
    public class Dog
        where TFavThing: DogToy

Or an interface type:

    public class Dog
        where TFavThing: IBuryable

Or another type parameter:

    // TFavThing must implement IBuryable
    // TOtherThing must be castable to TFavThing's type
    public class Dog<TFavThing,TOtherThing>
        where TFavThing: IBuryable
        where TOtherThing: TFavThing

class indicates that the type must be a reference type.

    public class Dog<TFavThing>
        where TFavThing: class

struct indicates that the type must be a non-nullable value type.

    public class Dog<TFavThing>
        where TFavThing: struct

new() indicates that the type must have a public parameterless constructor defined.

public class Dog<TFavThing>
    where TFavThing: new()

#546 – Specifying More than One Constraint for the Same Type Parameter

You can enforce a constraint on a type parameter for a generic class using the where keyword.  Following the where keyword, you typically indicate a type or interface that the actual parameter must adhere to.

You can define more than one constraint for the same type parameter.

In the example below, the TFavThing parameter must represent a type that implements both the IBuryable and IEdible interfaces.

    public class Dog<TFavThing>
        where TFavThing: IBuryable, IEdible
    {
        public void BuryThing(TFavThing thing)
        {
            thing.Bury();
        }

        public void Eat(TFavThing eatThis)
        {
            eatThis.Eat();
        }
            Dog<Bone> d = new Dog<Bone>("Buster", 5);

            Bone myBone = new Bone("Rawhide");
            d.BuryThing(myBone);
            d.Eat(myBone);

#545 – Specifying Constraints for More than One Type Parameter

When specifying constraints for type parameters in a generic class, you can specify constraints for more than one parameter.

To specify more than one constraint, just place each constraint on a separate line, with its own where clause.

    // TFavThing type parameter must be a type that implements IBuryable
    // TFavFood type parameter must be a type that implements IEdible
    public class Dog<TFavThing,TFavFood>
        where TFavThing: IBuryable
        where TFavFood: IEdible
    {
        public void BuryThing(TFavThing thing)
        {
            thing.Bury();
        }

        public void Eat(TFavFood eatThis)
        {
            eatThis.Eat();
        }
    }

When constructing this type, we just need to use types that implement the specified interfaces.

            // Bone implements IBuryable
            // RawFood implements IEdible
            Dog<Bone,RawFood> d = new Dog<Bone,RawFood>("Buster", 5);

            d.BuryThing(new Bone("Rawhide"));
            d.Eat(new RawFood(16));  // 16 oz chunk

#153 – Returning a Subset of Array Elements Matching a Particular Criteria

Because System.Array implements the IEnumerable interface and because LINQ extends IEnumerable, you can use the IEnumerable.Where method on arrays to find a subset of elements that match a particular criteria.

The Where method accepts a delegate to a function that takes a single element of the same type as the array and returns a boolean value, indicating whether a match is found.  Where returns an IEnumerable collection, which can be iterated on to get the elements in the subset.

Here’s an example, finding the set of passing scores in a set of scores.

            int[] scores = { 89, 98, 72, 100, 68 };

            // Count number of passing grades
            int numPassed = scores.Where((Func<int,bool>)IsPassingGrade).Count();

Here’s the implementation of IsPassingGrade.

        static bool IsPassingGrade(int score)
        {
            return (score >= 75);
        }

You can avoid defining a separate function by using a lamba expression.

            int numPassed = scores.Where(s => s >= 75).Count();

Similar to Array.FindAll.