#1,150 – Generic Delegate Type as Parameter

You can use a generic delegate type as a parameter to a method.

Suppose that we have the following type:

        public delegate T Merger<T>(T thing1, T thing2);

We can now create a method that takes a constructed version of this type as a parameter.  We could then call the method as shown in the Main method.

        static void MergeInts(Merger<int> intMerger, int n1, int n2)
        {
            int result = intMerger(n1, n2);
        }

        public static int Add(int n1, int n2)
        {
            return n1 + n2;
        }

        static void Main(string[] args)
        {
            MergeInts(Add, 5, 6);
        }

We could also define a generic method that has a generic delegate parameter.

        static void MergeSomething<T>(Merger<T> someMerger, T thing1, T thing2)
        {
            T result = someMerger(thing1, thing2);
        }

We can then call this method as follows:

        public static Dog Breed(Dog mama, Dog papa)
        {
                return new Dog(mama.Name + papa.Name);
        }

        static void Main(string[] args)
        {
            MergeSomething(Add, 5, 6);

            Dog dog1 = new Dog("Lassie");
            Dog dog2 = new Dog("Spuds");
            MergeSomething(Breed, dog1, dog2);
        }
Advertisements

#1,149 – Generic Delegate Types

A delegate type can be generic.  Consider the following delegate type:

        public delegate T Merger<T>(T thing1, T thing2);

This delegate type defines a pattern for methods that take two parameters of a particular type and returns the same type.  Below are a couple examples of methods whose signature matches this delegate type.  (Methods are only static so that we can call them from another static method).

        public static int Add(int n1, int n2)
        {
            return n1 + n2;
        }

        public static Dog Breed(Dog mama, Dog papa)
        {
                return new Dog(mama.Name + papa.Name);
        }

We can now declare Merger<T> instances that refer to these methods as follows:

            Merger<int> adder = Add;
            Merger<Dog> breeder = Breed;

#1,142 – Using EqualityComparer to Compare Objects in Generic Types

By default, you can’t use equality operators on objects whose type is a type argument within a generic class.  If you constrain an argument to be a reference type, you can use equality operators to do reference equality checks.  But you can‘t make use of overloaded equality functionality.

You can do a true equality check between objects using the EqualityComparer<T> class.  Below is an example.  EqualityComparer<T> makes use of overloaded equality logic in the Dog class, even though we haven’t constrained the T argument.

    public class Pile<T>
    {
        List<T> pile = new List<T>();
        EqualityComparer<T> comparer = EqualityComparer<T>.Default;

        public void Add(T item)
        {
            if (!pile.Contains(item))
                pile.Add(item);
        }

        public bool IsFirst(T item)
        {
            // Reference equality only, if we do
            //   where T : class
            //bool equalStd = (pile[0] == item);

            // Makes use of overloaded Equality
            // functionality in T
            bool equalBetter = comparer.Equals(pile[0], item);

            return equalBetter;
        }
    }

1142-001

#1,033 – Requiring a Generic Type Parameter to Have a Parameterless Constructor

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.

You can require that a type passed in as a type argument implement a public parameterless constructor by using the where keyword and specifying new().  The new() constraint tells the code that is constructing the type that it needs to only use types that have a parameterless constructor.

Without this constraint, you are not allowed to construct instances of the type passed in as a type parameter.  (The compiler will tell you to add the new() constraint).

With the constraint, your class might look like this:

    public class PileOf<T> where T : new()
    {
        private List<T> thePile;

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

            // First element in list is a default T
            thePile.Add(new T());
        }
    }

#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>();

#1,014 – Using default Operator in a Generic Class

The default operator provides a valid default value for any type.

One place where this operator is very handy is in a generic class, operating on the type parameter T.  In the example below, we initialize an internal collection of type T so that each element has the proper default value.

    public class BagOf<T>
    {
        private Collection<T> coll;
        public T SomeItem
        {
            get { return coll[0]; }
        }

        public BagOf(int numInBg)
        {
            if (numInBg <= 0)
                throw new Exception("Must have >0 items");

            coll = new Collection<T>();
            for (int i = 0; i < numInBg; i++)
                coll.Add(default(T));
        }
    }

Using this class, we can see the default values for each type of item.

            // Numeric
            BagOf<int> bagOfInt = new BagOf<int>(2);
            int anInt = bagOfInt.SomeItem;

            // Struct
            BagOf<Point3D> bagOfPoints = new BagOf<Point3D>(3);
            Point3D aPoint = bagOfPoints.SomeItem;

            // Reference type
            BagOf<Dog> bagOfDogs = new BagOf<Dog>(4);
            Dog aDog = bagOfDogs.SomeItem;

1014-001