#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,056 – Custom Implicit Conversions between Reference Types

You can define your own implicit conversions between value types.  You can also define a custom implicit conversion to allow implicitly converting from any type to a given reference type.

In the example below, we add methods to a Dog type to allow implicit conversions from both Cow and int types to the Dog type.

        // Implicitly convert from Cow to Dog
        public static implicit operator Dog(Cow cow)
        {
            return new Dog(string.Format(cow.Name + " IWasACow"), cow.Age);
        }

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

We can now do the following implicit conversions:

            Cow someCow = new Cow("Bessie", 3);

            // Cow becomes Dog
            Dog nowADog = someCow;

            // Number 42 becomes Dog
            Dog anotherDog = 42;

1056-001

#1,053 – Implicit Conversion from Type dynamic

You can implicitly convert from an expression of type dynamic to any other type.  What this means is that the compiler allows an implicit conversion (no cast operator) from the dynamic type to any other type and compilation succeeds.

While the conversion will always succeed at compile time, the actual assignment operation may fail at run-time, depending on whether a conversion between the actual type of the dynamic object and the target type succeeds.  The dynamic keyword tells the compiler, “wait until run-time to figure out the type”.

For example:

            dynamic dyn1 = "a string";
            dynamic dyn2 = 42;

            // Everything below succeeds at compile-time
            string s = dyn1;
            int i = dyn1;      // Fails at run-time (RuntimeBinderException)

            // (NOTE: Comment out above line to get
            //  past exception).
            s = dyn2;          // Fails at run-time (RuntimeBinderException)
            i = dyn2;

#1,051 – Implicit Reference Conversions

An implicit reference conversion is a conversion between reference types that is guaranteed to succeed and therefore does not require a cast operator.

Here are some examples:

            Dog dog = new Dog("Bowser");

            // From ref type to object
            object o1 = dog;

            // From ref type to dynamic
            dynamic dyn1 = dog;

            // From derived class to parent class
            Terrier terr = new Terrier("Jack");
            dog = terr;

            // From class to interface that class
            // implements
            IBark iBark = dog;

            // From a derived interface to
            // base interface
            IBarkWithStyle iBarkDerived = dog;
            iBark = iBarkDerived;

            // Covariant array conversion
            // Dog[] = Terrier[] ok because
            //    Dog = Terrier ok
            Terrier[] terriers = new Terrier[2];
            terriers[0] = new Terrier("Bobby");
            terriers[1] = new Terrier("Sydney");
            Dog[] dogs = terriers;

            // From array type to System.Array
            Array arr = terriers;

            // From array to IList<T>
            IList<Dog> dogList = terriers;

            // From any delegate-type to System.Delegate 
            // and the interfaces it implements
            MyStringHandlerDelegate del1 = HandleString;
            Delegate del2 = del1;

            // From null literal to any reference type
            Dog dog2 = null;

            // From any ref type to a ref type T if it has an 
            // implicit identity or reference conversion
            // to T0 and T0 has identity conversion to T.
            // E.g. can convert from Dog to object and
            //      from List to IEnumerable, so we can:
            List<Dog> somedogs = new List<Dog>();
            IEnumerable<object> genlist = somedogs;

#1,050 – Implicit Conversions between Nullable Types

You can implicitly convert from a non-nullable value type to a matching nullable type.  For example:

int i = 12;   // regular int, can't be null

int? j = 22;  // Nullable int, can store an int value

You can also implicitly convert between equivalent nullable types.  For example:

            int? nt1 = 5;
            int? nt2 = null;

            int? nt3 = nt1;  // ok
            nt3 = nt2;       // also ok

An implicit conversion is allowed between the nullable types (e.g. from int? to long?) if an implicit conversion exists between the corresponding base types (e.g. from int to long).

#1,049 – Full List of Implicit Numeric Conversions

You can do an implicit numeric conversion when the conversion would not result in the loss of data.  In general, you can go from a “smaller” type (smaller range of values) to a “bigger” type (larger range of values).

Here’s the full list of allowed implicit numeric conversions:

  • From sbyte to – short, int, long, float, double, decimal
  • From byte to –  short, ushort, int, uint, long, ulong, float, double, decimal
  • From short to – int, long, float, double, or decimal
  • From ushort to – int, uint, long, ulong, float, double, decimal
  • From int to – long, float, double, decimal
  • From uint to – long, ulong, float, double, decimal
  • From long to – float, double, decimal
  • From ulong to – float, double, decimal
  • From char to – ushort, int, uint, long, ulong, float, double, decimal
  • From float to – double