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

#1,026 – Checking a Flagged enum Type for Validity

You normally use the Enum.IsDefined method to check whether a particular enum value is a valid value.  This method does not work on flagged enums.

Assuming the following enum type:

    [Flags]
    public enum Talents
    {
        Singing = 1,
        Dancing = 2,
        Juggling = 4,
        JokeTelling = 8
    };

Enum.IsDefined will not work for all values of the flagged enum:

            Talents myTalent = Talents.JokeTelling | Talents.Juggling;
            // Returns false
            bool isDefCheck = Enum.IsDefined(typeof(Talents), myTalent);

To properly check for validity, you can convert the enum to a string and then try parsing as an integer. If the value is invalid, ToString() will convert to a simple integer and the parse will then succeed.

        private static void CheckTalents(Talents t)
        {
            int intTest;
            if (int.TryParse(t.ToString(), out intTest))
                Console.WriteLine("Talents not ok");
            else
                Console.WriteLine("Talents ok");
        }

1026-001

#1,025 – Converting between enum Types

You can convert between two different enum types as long as they both use the same underlying type (e.g. int).  You use an explicit cast to do the conversion.

Assuming two enum types that by default use int as their underlying type, e.g.:

    public enum Day { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday };
    public enum Dinner { Sausages, Mutton, Tacos, Wontons, ThinMints, FriedEggs, Sandwiches }

You can convert between them using a simple cast:

            Day today = Day.Monday;
            Dinner menuTonight = (Dinner)today;
            Console.WriteLine("Today is {0} and I'm having {1} for dinner", today, menuTonight);

1025-001

#1,016 – Retrieving the Length of an Array

You can use the Length property of an array to get the number of elements in the array.

For a one- or multi-dimensional arrays, the length is always the total number of elements.  For a jagged array (array of arrays), the length is the number of elements in the outer dimension.

            int[] someNumbers = { 5, 16, 12, 38, 78, 63 };
            // Length = 6
            Console.WriteLine(someNumbers.Length);

            int[,] twoDimensional = { { 3, 2, 1 }, { 1, 2, 3 }, { 4, 6, 8 } };
            // Length = 9
            Console.WriteLine(twoDimensional.Length);

            int[][] jagged = new int[3][] {
                new int[] { 1, 2, 3 },
                new int[] { 4, 8 },
                new int[] { 10, 20, 30, 40 } };
            // Length = 3
            Console.WriteLine(jagged.Length);

1016-001

#1,015 – Rendering a Byte Array as a Series of Hex Characters

You can use the BitConverter class to get a hex representation of a string of bytes.

For example:

            byte[] someBytes = { 0x83, 0xDF, 0x0A, 0xA3, 0x92 };

            string hex = BitConverter.ToString(someBytes);
            Console.WriteLine(hex);

1015-001