#1,138 – Type Parameters in Generic Methods Can Be Constrained

You can constrain the type parameters in a generic class in a number of different ways.  You can also constrain type parameters in generic methods, using the same types of constraints.

In the example below, we define a generic method Bury<T> in the Dog class.  We constrain the type parameter, indicating that it should implement the IBuryable interface.

    public class Dog
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        // Generic method
        public void Bury<T>(T thing) where T: IBuryable
        {
            Console.WriteLine("{0} is burying: {1}", Name, thing);
        }
    }

We can pass the Bury method any object that implements IBuryable.

            Dog d = new Dog("Bowser");
            d.Bury(new Bone());

If we try passing an object that does not implement IBuryable, we’ll get a compile-time error, indicating that the compiler can’t convert to IBuryable.

1138-001

Advertisement

#1,136 – Overloading a Generic Method

In the same way that you can overload a generic class, you can overload a generic method, defining multiple generic methods having the same name but different type parameters.  You can also define non-generic methods with the same name.

Below, we overload the Dog.Bury method, defining several non-generic and several generic methods.

    public class Dog
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        public void Bury(Bone b)
        {
            Console.WriteLine("{0} is burying: {1}", Name, b);
        }

        public void Bury(Lawyer l)
        {
            Console.WriteLine("{0} is burying: {1}", Name, l);
        }

        public void Bury<T>(T thing)
        {
            Console.WriteLine("{0} is burying: {1}", Name, thing);
        }

        public void Bury<T>(T thing, string msg)
        {
            Console.WriteLine("{0} : {1}", msg, thing);
        }

        public void Bury<T1, T2>(T1 thing1, T2 thing2)
        {
            Console.WriteLine("{0} is burying: {1}", Name, thing1);
            Console.WriteLine("{0} is burying: {1}", Name, thing2);
        }
    }

We can call these methods as follows:

            Dog fido = new Dog("Fido");

            fido.Bury(new Bone());
            fido.Bury(new Lawyer());
            fido.Bury<Cow>(new Cow("Bessie"));
            fido.Bury<Lawyer>(new Lawyer(), "One less lawyer");
            fido.Bury<Cow,Cat>(new Cow("Bessie"), new Cat("Puffy"));

#1,038 – Type Parameter Constraints on Generic Methods

You can specify type parameter constraints for both generic types and generic methods.

The example below shows a type constraint for a generic type.

    public class MooPile<T> where T : IMoo
    {
        private List<T> mooPile = new List<T>();

        public void AddMooThing(T thing)
        {
            mooPile.Add(thing);
            mooPile[mooPile.Count - 1].Moo();
        }
    }

And below is a type constraint for a generic method (which happens to be defined within a non-generic class).

        static void SwapAndMoo<T>(ref T item1, ref T item2) where T : IMoo
        {
            T temp = item1;
            item1 = item2;
            item2 = temp;

            item1.Moo();
            item2.Moo();
        }

#1,028 – Generic Types vs. Generic Methods

generic type is a type that uses a type parameter in its definition, which must be concretely specified when constructing the type.

    public class PileOf<T>
    {
        private List<T> thePile;

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

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

A generic method is a method that introduces a generic type parameter.

        static void Swap<T>(ref T item1, ref T item2)
        {
            T temp = item1;
            item1 = item2;
            item2 = temp;
        }

Methods within generic types that use the type parameter from the type are not consider generic.  The AddItem method in the PileOf<T> class above is not considered generic.  To be considered a generic method, the method must introduce a new type parameter not present as a type parameter in the containing class.

 

#540 – Non-Generic Methods in a Generic Class

You can have both generic and non-generic methods in a generic class.  The non-generic methods can still make use of the class’ generic type parameters.

In the example below, the BuryThing method accepts a parameter whose type matches the type parameter of the class itself.

    public class Dog<T>
    {
        // Not classified as generic method because it does not introduce new type parameter
        public void BuryThing(T thing)
        {
            Console.WriteLine(string.Format("{0} is burying: {1}", Name, thing.ToString()));
        }

When we invoke this method, we must pass it an object whose type matches the type parameter that the class was constructed with.

            Dog<Cow> d = new Dog<Cow>("Buster", 5);
            d.BuryThing(new Cow("Bessie"));

#539 – Type Inference When Calling Generic Methods

When you call a generic method, you can provide a type argument, indicating the type for any type parameters, or you can let the type be inferred by the compiler.

For the following type definition:

public static void DogBuriesThing<T>(Dog d, T thing)

You can either provide the type argument:

            Dog buster = new Dog("Buster", 5);
            Dog.DogBuriesThing<Bone>(buster, new Bone("Buster's rawhide"));

Or you can omit the type when you call the method, letting the type be inferred based on the type of the second argument:

            Dog.DogBuriesThing(buster, new Cow("Bessie"));

#538 – Implement a Generic Method

In addition to classes, structs and interfaces, a method can also be generic.  A generic method is one that is declared with type parameters.  A generic method can be either a static method or an instance method.

In the example below, notice that both methods are generic, though the Dog class itself is not generic.

    public class Dog
    {
        // Generic static method
        public static void DogBuriesThing<T>(Dog d, T thing)
        {
            Console.WriteLine(string.Format("{0} is burying: {1}", d.Name, thing.ToString()));
        }

        // Generic instance method
        public void BuryThing<T>(T thing)
        {
            Console.WriteLine(string.Format("{0} is burying: {1}", Name, thing.ToString()));
        }

Examples of calling these methods:

            // Call generic static method
            Dog buster = new Dog("Buster", 5);
            Dog.DogBuriesThing<Bone>(buster, new Bone("Buster's rawhide"));

            // Call generic instance method
            buster.BuryThing<Cow>(new Cow("Bessie"));