#1,065 – Cases When Array Covariance Doesn’t Work

Array covariance in C# allows you to assign an array of objects of a more derived type to an array of objects of a base type.  An array of type B can be assigned to an array of type A is type B is implicitly convertible to A.  More specifically, an implicit reference conversion from B to A must exist.

This restriction means that array covariance will not work for implicit conversions that are not implicit reference conversions.  Specifically, arrays are not covariant for implicit numeric conversions, boxing conversions or implicit custom conversions.

            // Covariance does work when implicit reference conversion
            // exists for underlying types
            //   - From S to T when S derives from T
            //   - From S to object or dynamic
            //   - From S to interface-type T when S implements T
            //   - From interface-type S to interface-type T when S derives from T
            // All examples below work (compile)
            Terrier[] tarr1 = new Terrier[2];
            Dog[] darr1 = tarr1;
            object[] oarr1 = tarr1;
            dynamic[] dynarr1 = tarr1;
            Dog[] darr2 = new Dog[2];
            IBark[] ibarr1 = darr2;
            IBarkBetter[] ibb1 = new IBarkBetter[2];
            IBark[] ibarr2 = ibb1;

            // Covariance does NOT work with implicit numeric conversions
            //  e.g. byte to short
            byte[] barr1 = new byte[2];
            short[] sarr1 = barr1;  // Compile-time error

            // Covariance does NOT work with implicit nullable conversions
            int[] inarr1 = new int[2];
            int?[] innarr1 = inarr1;  // Compile-time error

            // Covariance does NOT work with implicit boxing conversions
            int[] inarr2 = new int[2];
            object[] oarr2 = inarr2;  // Compile-time error

            // Covariance does NOT work with implicit custom conversions
            Cow c = new Cow("Bessie");
            Dog d = c;  // Implicit Cow to Dog works (custom conversion)
            Cow[] herd = new Cow[2];
            Dog[] pack = herd;  // Compile-time error
Advertisement

#1,058 – Custom Implicit Conversions in Both Directions

When you define a custom implicit (or explicit) conversion, you can define a conversion both to and from a particular type.

In the example below, we define custom implicit conversions that allow implicitly converting from an int to a Dog, as well as converting from a Dog to an int.

    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

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

        // And implicitly convert from Dog to int
        public static implicit operator int(Dog d)
        {
            return d.Age;
        }
    }

With these conversions, we can now do the following:

            // int to Dog
            int i1 = 12;
            Dog dog1 = i1;

            // Dog to int
            Dog dog2 = new Dog("Bowser", 5);
            int i2 = dog2;

1058-001