#1,064 – Getting Around Inability to Explicitly Convert Type Parameters

You can’t explicitly convert a type parameter to either a value type or a reference type (you can convert to an interface).

To get around this restriction, you can use the as operator on the type parameter.  This gives you a way to effectively convert the type parameter, get the code to compile, and avoid runtime exceptions.

    class Program
    {
        public class ThingContainer<T>
        {
            private T thing;

            public void SetThing(T t)
            {
                thing = t;

                // Won't compile
                //int i = (int)t;

                // Do this instead
                int? i = t as int?;
                if (i.HasValue)
                    Console.WriteLine("Your int: " + i);

                // Won't compile
                //Dog d = (Dog)t;

                // Do this instead
                Dog d = t as Dog;
                if (d != null)
                    Console.WriteLine("Your Dog: " + d.Name);
            }
        }

        static void Main(string[] args)
        {
            ThingContainer<int> intcont = new ThingContainer<int>();
            intcont.SetThing(5);

            ThingContainer<Dog> dogcont = new ThingContainer<Dog>();
            dogcont.SetThing(new Dog("Bowser"));

            ThingContainer<Cow> cowcont = new ThingContainer<Cow>();
            cowcont.SetThing(new Cow("Bessie"));

            Console.ReadLine();
        }
    }

1064-001

#1,063 – Explicit Conversions and Type Parameters

You can’t explicitly convert a type parameter to either a reference type or a value type.  Casts to reference or value types are disallowed at compile-time.  The compiler allows casting the type parameter to an interface type and this conversion will succeed at run-time if the object implements that interface.

        public class ThingContainer<T>
        {
            private T thing;

            public void SetThing(T t)
            {
                thing = t;

                // Won't compile
                int i = (int)t;

                // Won't compile
                Dog d = (Dog)t;

                // Will compile, but throw
                // InvalidCastException at run-time
                // if T doesn't implement IBark
                IBark ib = (IBark)t;
            }
        }

        static void Main(string[] args)
        {
            ThingContainer<Dog> dogcont = new ThingContainer<Dog>();
            dogcont.SetThing(new Dog("Bowser"));

            ThingContainer<Cow> cowcont = new ThingContainer<Cow>();
            cowcont.SetThing(new Cow("Bessie"));

            Console.ReadLine();
        }

#1,062 – Unboxing Conversions

If you’ve converted a value-typed object to a reference-typed object by boxing it, you can later unbox the object, converting it back to a value type.  Unboxing is an explicit conversion and requires a cast operator.

Below are some examples of unboxing conversions.

        public interface IArea
        {
            double CalcArea();
        }

        public struct MyPoint : IArea
        {
            public double X;
            public double Y;

            public MyPoint(double x, double y)
            {
                X = x;
                Y = y;
            }

            public double CalcArea()
            {
                return X * Y;
            }
        }

        static void Main(string[] args)
        {
            int i1 = 12;
            object o = i1;  // Boxing - implicit

            // Unbox from object to value type
            int i2 = (int)o;  // Boxing - explicit conversion

            // Unbox from dynamic
            dynamic d = i1;
            int i3 = (int)d;

            // Boxing, implicit, creates new copy
            MyPoint pt = new MyPoint(2.0, 3.0);
            IArea area = pt;

            // Unboxing, explicit,
            // from interface to value type,
            // creates new copy again
            MyPoint pt2 = (MyPoint)area;

            // Unbox to nullable type
            int? i4 = (int?)o;
            o = null;
            int? i5 = (int?)o;  // also works

            // Boxing, implicit, to ValueType
            // (creates copy)
            ValueType vty = pt;

            // Unboxing, creates copy
            MyPoint pt3 = (MyPoint)vty;
        }

#1,061 – Explicit Reference Conversions

An explicit reference conversion is a conversion between reference types that requires a cast operator.  Explicit reference conversions are not guaranteed to succeed and require a check at run-time to determine whether the conversion is allowed.  Because the check is done at run-time, the cast operator allows the conversion at compile-time.

Here are some examples:

        delegate void StringDel(string info);

        static void DelMethod(string info) { }

        static void Main(string[] args)
        {
            object o1 = new Dog("Bowser");
            dynamic dyn = o1;
            object o2 = new Cow("Bessie");
            Dog d2 = new BorderCollie("Shep");

            // From object or dynamic to ref type
            Dog d = (Dog)o1;
            d = dyn;

            // From base class to more derived type
            BorderCollie bc1 = (BorderCollie)d2;

            // From base class to interface that the
            // class does not implement, but that
            // the actual object does implement.
            // E.g. Dog does not implement IRun,
            //   but BorderCollie inherits from
            //   Dog and does implement IBark.
            //   d2 is of type Dog, but contains
            //   a BorderCollie.
            IRun ir1 = (IRun)d2;

            // From interface type to class that
            // implements the interface
            BorderCollie bc2 = (BorderCollie)ir1;

            // From one interface to the other, provided
            // that underlying object implements the
            // target interface.
            // E.g. BorderCollie implements both IRun
            //   and IFetch, ir1 is of type IRun but
            //   refers to a BorderCollie.  We can
            //   convert from variable of type IRun
            //   to one of type IFetch
            IFetch if1 = (IFetch)ir1;

            // From one array to another, provided that
            // explicit conversion exists between element
            // types.
            Dog[] dogs = { new Dog("Shep"), new Dog("Kirby") };
            object[] dogsAsObjs = dogs;
            Dog[] dogs2 = (Dog[])dogsAsObjs;

            // From System.Array to a specific array type
            Array arrints = new int[2];
            int[] ints = (int[])arrints;

            // From array to IList<>, if explicit conversion from
            // array element type to IList type exists.
            object[] objs = new Dog[2];
            IList<Dog> dogList = (IList<Dog>)objs;

            // From IList<> to array, if explicit conversion from
            // IList type to array element type exists
            Dog[] objs2 = new Dog[2];
            IList<object> dogList2 = objs2;  // implicit
            Dog[] dogs3 = (Dog[])dogList2;   // explicit

            // From Delegate to a specific delegate type
            StringDel myDel = DelMethod;
            Delegate genDel = myDel;  // implicit
            StringDel myDel2 = (StringDel)genDel;  // explicit
        }

#1,060 – Explicit Conversions between Nullable Types

An implicit conversion exists between two nullable types if an implicit conversion exists between the corresponding value types.  For example:

int i = 12;
long l = i;  // implicit int to long
int? i2 = 12;
long? l2 = i2;  // implicit int? to long?

Similarly, an explicit conversion exists between two nullable types if an explicit conversion exists between the corresponding value types.

long l = 12;
int i = (int)l;  // explicit long to int
long? l2 = 12;
int? i2 = (int?)l2;  // explicit long? to int?

You can also convert between a nullable and non-nullable type, either implicitly or explicitly.  An explicit conversion is required when converting from a nullable type to a non-nullable type or when an explicit conversion is required by the underlying types.

            int? i3 = (int?)l;   // explicit long to int?
            long l3 = (long)i3;  // explicit int? to long

#1,059 – Converting from Numeric to Enumerated Types

You can convert from a numeric type to an enumerated type using the cast operator.  For example, to convert from numeric constants to an enum, we can do the following:

        public enum Weekday
        {
            Sunday = 1,
            Monday,     // 2
            Tuesday,    // 3, etc.
            Wednesday,
            Thursday,
            Friday,
            Saturday
        };

        static void Main(string[] args)
        {
            Weekday day = (Weekday)5;  // Thursday
            Console.WriteLine(day.ToString());  // "Thursday"

            Weekday day2 = (Weekday)8;    // Works!
            Console.WriteLine(day2.ToString());  // Just "8"
        }

Notice that the cast works even if there is not a defined enumerated value that matches the specified type. We can store a value of 8 in a Weekday type even though we didn’t define a Weekday with a value of 8.

1059-001

#1,058 – Custom Implicit Conversions in Both Directions

When you define a custom implicit (or explicit) conversion, you can define a conversion both to and from a particular type.

In the example below, we define custom implicit conversions that allow implicitly converting from an int to a Dog, as well as converting from a Dog to an int.

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

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

        // Implicitly convert from int to Dog
        public static implicit operator Dog(int value)
        {
            return new Dog(string.Format("Dog-" + value.ToString()), value);
        }

        // And implicitly convert from Dog to int
        public static implicit operator int(Dog d)
        {
            return d.Age;
        }
    }

With these conversions, we can now do the following:

            // int to Dog
            int i1 = 12;
            Dog dog1 = i1;

            // Dog to int
            Dog dog2 = new Dog("Bowser", 5);
            int i2 = dog2;

1058-001