#1,070 – A Generic Type Can Be Compiled

Recall that a generic type is a template for a class that you can define later.  The generic type contains type parameters that act as placeholders for type arguments that you supply when you define a constructed type based on the generic type.  Without defining the constructed type, the type parameters in the generic type have no assigned type.  That is, they can be of any type (unless some constraints have been specified for the type parameter).

You can compile a particular generic type without have any constructed types that make use of the generic type.  For example, List<T> is compiled and included in mscorlib.dll.  This allows you to later use the generic type in your own code.

Advertisements

#1,044 – How Static Data Behaves in Generic Types

There is only a single copy of each static data item, rather than one copy for each instance of the class.

When you have a static data item in a generic class, there is a single copy of that data item for each constructed type based on the generic type.  A constructed type is a type declaration that is based on a generic type, providing arguments for the generic type’s type parameters.

Assume that we define a PileOf<T> type that has a static NumInAllPiles field, incremented as part of an Add method.  We reference the static field using the constructed class name and see that each constructed type has its own copy of the static data.

            // Pile of 1 dog
            PileOf<Dog> pile1 = new PileOf<Dog>();
            pile1.Add(new Dog("Bowser"));

            // Pile of 2 dogs
            PileOf<Dog> pile2 = new PileOf<Dog>();
            pile2.Add(new Dog("Kirby"));
            pile2.Add(new Dog("Fido"));

            // Pile of 1 cat
            PileOf<Cat> pile3 = new PileOf<Cat>();
            pile3.Add(new Cat("Fluffy"));

            Console.WriteLine(PileOf<Dog>.NumInAllPiles);  // 3
            Console.WriteLine(PileOf<Cat>.NumInAllPiles);  // 1

#1,043 – Deriving from a Self-Referencing Constructed Type, part II

When defining a type, you can derive from a constructed generic type that takes the defining type as its type argument.  This technique allows extending the behavior of some base class, but with the advantages of using generics.

    public class CreaturesParents<T>
    {
        public T Mom { get; set; }
        public T Dad { get; set; }

        public string ParentInfo()
        {
            return string.Format("Mom: {0}, Dad: {1}", Mom.ToString(), Dad.ToString());
        }
    }

    public class Dog : CreaturesParents<Dog>
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public override string ToString()
        {
            return string.Format("{0} is {1} yrs old", Name, Age);
        }
    }

Example of using this type:

            Dog fido = new Dog("Fido", 4);
            Dog lassie = new Dog("Lassie", 12);
            Dog bowser = new Dog("Bowser", 9);

            fido.Mom = lassie;
            fido.Dad = bowser;

            Console.WriteLine(fido.ParentInfo());

1043-001

#1,042 – Deriving from a Self-Referencing Constructed Type, Part I

When defining a type, the type can derive from a constructed generic type.

public class DogList : List<string>

You can use the name of the type being defined as the type argument to the type (or interface) that you’re deriving from.

Below, the Dog type is an argument to the IComparable generic interface.  We’re saying that we want Dog to implement IComparable<T> and it makes sense that the argument should be Dog.

    public class Dog : IComparable<Dog>
    {
        public string Name { get; set; }
        public int WoofPower { get; set; }

        public Dog(string name, int woofPower)
        {
            Name = name;
            WoofPower = woofPower;
        }

        public int CompareTo(Dog other)
        {
            // Returns
            //   < 0 if this < other
            //   0 is this == other
            //     > 0 if this > other
            return this.WoofPower - other.WoofPower;
        }
    }

Using Dog:

            Dog fido = new Dog("Fido", 4);
            Dog bowser = new Dog("Bowser", 9);

            int compare = fido.CompareTo(bowser);

#1,041 – Adding New Type Parameters when You Derive from a Generic Class

You can define a generic class that inherits from another generic class, for example:

public class ListPlus<T> : List<T>

You can also add new type parameters in the new class being defined.  In the example below, ListPlus derives from List<T> and adds a second type parameter.

        public class ListPlus<T, TShadow> : List<T>
        {
            private List<TShadow> shadowList = new List<TShadow>();

            public void AddItem(T mainItem, TShadow shadowItem)
            {
                base.Add(mainItem);
                shadowList.Add(shadowItem);
            }
        }

When we construct the type, we need to provide type arguments for both type parameters.

            ListPlus<Dog, Cat> myListPlus = new ListPlus<Dog, Cat>();
            Dog fido = new Dog("Fido");
            Cat puff = new Cat("Puff");
            myListPlus.AddItem(fido, puff);

#1,040 – Deriving from a Constructed Type

You can define a generic class that inherits from another generic class, for example:

public class ListPlus<T> : List<T>

You can also derive from a constructed type, rather than a generic type.  A constructed type is just a generic type with the type parameters filled in (type arguments provided).

For example:

    public class DogListPlus : List<Dog>
    {
        public void AddTwoDogs(Dog d1, Dog d2)
        {
            base.Add(d1);
            base.Add(d2);
        }
    }

We can then use this new type as follows:

            DogListPlus dogList = new DogListPlus();
            Dog fido = new Dog("Fido");
            Dog bowser = new Dog("Bowser");
            dogList.AddTwoDogs(fido, bowser);

#1,039 – Deriving from a Generic Class

A generic class includes one or more type parameters, allowing you to later declare a variable using a constructed type by specifying type arguments for each type parameter.

If you have an existing generic class, you can define a new class that inherits from the generic class.

Assume that we have a List<T> type, i.e. we can construct the type as follows:

            List<int> someInts = new List<int>();

We can also define a new class that inherits from List<T> and makes use of its type parameter as follows:

    public class ListPlus<T> : List<T>
    {
        public void AddTwoItems(T thing1, T thing2)
        {
            base.Add(thing1);
            base.Add(thing2);
        }
    }

We can now construct the new type by providing a type argument.

            ListPlus<Dog> dogs = new ListPlus<Dog>();
            Dog fido = new Dog("Fido");
            Dog bowser = new Dog("Bowser");
            dogs.AddTwoItems(fido, bowser);