#1,054 – Implicit Conversions from Constant Expressions

A constant expression is an expression whose type and value can be determined at compile-time.  Integer literals are one form of a constant expression, with the type inferred based on the value of the literal (and the use of u and/or suffixes).

Recall that implicit numeric conversions are not allowed if the full range of values in the expression is not allowed in the target type.  For example, you can’t do an implicit conversion from a variable of type int to a byte, even if the variable contains a value that could be stored in a byte.

            int i = 1;

            // Implicit conversion not allowed,
            // i.e. requires a cast
            byte b = i;

Implicit conversions from constant expressions, however, are allowed as long as the value is within the range of the target type. Since the compiler knows the value at compile-time, it can make this determination.

For example, an integer literal whose value of 1 will be of type int, but is implicitly convertible to a variable of type byte.

            // Literal 1 is of type
            // int (inferred by compiler),
            // but can be implicitly converted
            // to byte.
            byte b2 = 1;
Advertisement

#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,052 – Boxing Is a Kind of Implicit Conversion

Boxing is the process of copying a value-typed object to a new instance of a reference-typed object.  Boxing operations are classified as implicit conversions, in that you are converting between types in a way that is guaranteed to succeed and does not require a cast operator.

Below are some examples of boxing operations.

        public interface IDistance
        {
            double CalcDistance();
        }

        public struct Point3D : IDistance
        {
            public double X, Y, Z;

            public double CalcDistance()
            {
                return Math.Sqrt(X*X + Y*Y + Z*Z);
            }
        }

        public enum Mood { Crabby, NotSoCrabby };

        static void Main(string[] args)
        {
            // Three kinds of value-typed objects
            Point3D pt;
            pt.X = 1.0;
            pt.Y = 2.0;
            pt.Z = 3.0;

            int i1 = 12;
            int? i2 = null;

            // Convert to object
            object o = pt;
            o = i1;
            o = i2;

            // Convert to dynamic
            dynamic dyn1 = pt;
            dyn1 = i1;
            dyn1 = i2;

            // Convert to System.ValueType
            ValueType vty = pt;
            vty = i1;
            vty = i2;

            // Convert to interface implemented
            // by the value type
            IDistance idist = pt;
            double dist = idist.CalcDistance();

            // From enum value to System.Enum
            Mood myMood = Mood.NotSoCrabby;
            Enum genEnum = myMood;
        }

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

#1,045 – Implicit Conversions When Assigning from a Nullable Type

Implicit conversions are allowed when converting from a value type to its associated nullable type.  Implicit conversions are not allowed in the other direction, however, from nullable type to corresponding value type.  This is because the range of the nullable type is greater than the range of the associated value type.  That is, the conversion may result in a loss of data.  You must either use an explicit conversion or access the underlying value using the Value property.

            int anInt = 12;

            // Allowed - implicit conversion to nullable type
            int? nullableInt = anInt;

            // Also allowed, since implicit conversion from int to long
            // is allowed
            long? nullableLong = anInt;

            // Implicit conversion not allowed
            //int newInt = nullableInt;

            // Must do explicit conversion
            int newInt = (int)nullableInt;

            // Better
            newInt = nullableInt.HasValue ? nullableInt.Value : 0;

            // long? = int? implicit conversion IS allowed
            nullableLong = nullableInt;