#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

#1,048 – No Implicit Conversions between Signed vs. Unsigned

You can do an implicit numeric conversion when the conversion would not result in the loss of data.

You can never do an implicit conversion to convert between the signed vs. unsigned variants of a particular type, e.g. between short and ushort.

This makes sense, because the ranges of the signed vs. unsigned types overlap, but one is not inclusive of the other.  For example:

  • short :  -32,768 to 32,767
  • ushort :  0 to 65,535

You can’t do an implicit conversion between these types, in either direction.

You can, however, convert between these types using an explicit conversion (with a cast operator).  If the value to be converted is not representable in the target type, data will be lost but the assignment will succeed.  (How explicit casts fail).

            ushort myUnsignedShort = 40000;
            short myShort;

            // Compiles, but data is lost on
            // assignment
            myShort = (short)myUnsignedShort;

            // Now we'll get OverflowException
            myShort = checked((short)myUnsignedShort);

Here’s what the data looks like after the first assignment:
1048-001

#1,047 – The Implicit Identity Conversion

One of the implicit conversions that exists in C# is the identity conversion, which states that an expression of a given type can be implicitly converted to that same type.

This sounds silly and unnecessary, but allows the compiler to know that assignments like the one below are allowed.

            int anInt = 12;

            // Identify conversion allows the
            // following assignment as an
            // implicit conversion
            int another = anInt;

#1,046 – Implicit vs. Explicit Conversions

conversion occurs in C# when you convert an expression of one type to a different type.  Often this occurs when you convert an instance of one type to an instance of another type.

Conversions can generally be broken down into two categories:

  • Implicit
    • Conversion can happen automatically
    • Guaranteed to succeed
    • No loss of data
  • Explicit
    • Requires a cast operator
    • Required when either chance of failure or when there will be data loss

Below are some examples of both numeric and reference conversions, implicit and explicit.

            int anInt = 12;

            // Implicit numeric conversion
            long aLong = anInt;

            // Explicit numeric conversion
            int newInt = (int)aLong;

            BorderCollie kirby = new BorderCollie("Kirby", 12);

            // Implicit reference conversion
            // - derived class to base class
            Dog d = kirby;

            // Explicit reference conversion
            // -  base class to derived class, may fail
            // (This example will throw an InvalidCastException
            //  because we're trying to convert a BorderCollie to a
            //  Terrier).
            Terrier t = (Terrier)d;