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

#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