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

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

#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,029 – How to Define a Constructor in a Generic Type

When you define a generic type, you include one or more type parameters in the declaration of the type, indicating the type of the type arguments that you specify when constructing an instance of the type.

As with other types, you can define one or more constructors in a generic type.  The constructor uses the name of the type, but without the associated type parameters.  You can, however, have constructors that accept parameters whose type is one of the type parameters.

In the example below, the second constructor makes use of the T type parameter.

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

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

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

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

We can then use this type as follows:

            PileOf<Dog> intPile = new PileOf<Dog>(new Dog("Bowser"));
            intPile.AddThing(new Dog("Fido"));

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

 

#1,027 – Type Parameters vs. Type Arguments in a Generic Class

generic class is a class that takes one or more type parameters, which it then uses in the definition of the class.  It can be thought of as a template for a class.

Type parameters are used in the definition of the type.  In the code below, T1 and T2 are type parameters.

    public class ThingContainer<T1, T2>
    {
        private T1 thing1;
        private T2 thing2;

        public void SetThings(T1 first, T2 second)
        {
            thing1 = first;
            thing2 = second;
        }

        public string DumpThings()
        {
            return string.Format("{0}, {1}", thing1.ToString(), thing2.ToString());
        }
    }

You provide a type argument for each type parameter when you declare an instance of the generic type, constructing the type.  In the sample code below, string and int are the type arguments that map to the T1 and T2 type parameters.

            ThingContainer<string, int> cont1 = new ThingContainer<string, int>();
            cont1.SetThings("Hemingway", 1899);
            Console.WriteLine(cont1.DumpThings());      //  Hemingway, 1899

#1,026 – Checking a Flagged enum Type for Validity

You normally use the Enum.IsDefined method to check whether a particular enum value is a valid value.  This method does not work on flagged enums.

Assuming the following enum type:

    [Flags]
    public enum Talents
    {
        Singing = 1,
        Dancing = 2,
        Juggling = 4,
        JokeTelling = 8
    };

Enum.IsDefined will not work for all values of the flagged enum:

            Talents myTalent = Talents.JokeTelling | Talents.Juggling;
            // Returns false
            bool isDefCheck = Enum.IsDefined(typeof(Talents), myTalent);

To properly check for validity, you can convert the enum to a string and then try parsing as an integer. If the value is invalid, ToString() will convert to a simple integer and the parse will then succeed.

        private static void CheckTalents(Talents t)
        {
            int intTest;
            if (int.TryParse(t.ToString(), out intTest))
                Console.WriteLine("Talents not ok");
            else
                Console.WriteLine("Talents ok");
        }

1026-001

#1,025 – Converting between enum Types

You can convert between two different enum types as long as they both use the same underlying type (e.g. int).  You use an explicit cast to do the conversion.

Assuming two enum types that by default use int as their underlying type, e.g.:

    public enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
    public enum Dinner { Sausages, Mutton, Tacos, Wontons, ThinMints, FriedEggs, Sandwiches }

You can convert between them using a simple cast:

            Day today = Day.Monday;
            Dinner menuTonight = (Dinner)today;
            Console.WriteLine("Today is {0} and I'm having {1} for dinner", today, menuTonight);

1025-001

#1,024 – Exposing internal Members to Another Assembly

Internal class members are accessible within the class in which they are defined, as well as from other code within the same assembly.

Suppose that we define an internal field as follows:

    public class Dog
    {
        internal static int DogCount = 0;

        public string Name;

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

Code within another assembly that is using the Dog class will not be able to access the DogCount property.

It’s sometimes useful, however, to make internal members accessible to testing code that exists in another assembly.  We can do this with the InternalsVisibleTo attribute.  In the example above, we can define the following within AssemblyInfo.cs in the library containing the Dog class.

[assembly: InternalsVisibleTo("DogTester")]

Code within the DogTester assembly can now access internal members in Dog.

            Dog d = new Dog("Kirby");
            Dog d2 = new Dog("Jack");

            Console.WriteLine("We've created {0} dogs", Dog.DogCount);