views:

280

answers:

4

Hi,

if I have two classes, and have defined an explicit type conversion between them, should I not be able to convert an array of one to an array of the other ?

ie.

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Apple ted = new Apple() { Variety = Apple.EVariety.RedEllison };
            Orange bob = (Orange)ted; // explicit type conversion

            Apple[] apples = new Apple[] { ted };
            Orange[] oranges = new Orange[1];

            //oranges = apples; // why is this illegal ?

            // is this better ?
            oranges = Array.ConvertAll<Apple, Orange>(apples, new Converter<Apple, Orange>(p => (Orange)p));
        }

        class Apple
        {
            public enum EVariety { RedEllison, GrannySmith }
            public EVariety Variety;
        }

        class Orange
        {
            enum EColour { Unknown, Red, Green }
            EColour Colour;

            public static explicit operator Orange(Apple apple)
            {
                Orange result = new Orange();
                result.Colour = apple.Variety == Apple.EVariety.RedEllison ? result.Colour = EColour.Red : result.Colour = EColour.Green;
                return result;
            }
        }
    }
}

Thanks, Ross

A: 

I prefer the extension methods in System.Linq:

oranges = apples.Cast<Oranges>().ToArray();


Edit:

Since Cast does not work with implicit casting operators, I would use select instead:

oranges = apples.Select(x => (Oranges)x).ToArray();

The difference to you code is that this creates a new array, while yours is creating the array first and copying the casted references afterwards.

Stefan Steinegger
Does LINQ's .Cast really invoke explicit cast operators?
dtb
@dtb: acording to this: http://stackoverflow.com/questions/808725/why-does-a-linq-castt-operation-fail-when-i-have-an-implicit-cast-defined it does not. I fix it.
Stefan Steinegger
+2  A: 

The conversion is only valid if there's a reference conversion between the source and the target types - in other words, one where each element in the array can be used as either type, with no changes to the data.

From the C# spec, section 6.2.4 (explicit reference conversions):

The explicit reference conversions are:

...

  • From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true:
    • S and T differ only in element type. In other words, S and T have the same number of dimensions.
    • Both SE and TE are reference-types.
    • An explicit reference conversion exists from SE to TE.

In my opinion, Array.ConvertAll is definitely the way to go here, but you should be able to use type inference to make it nicer:

oranges = Array.ConvertAll(apples, p => (Orange)p);
Jon Skeet
Ooohh !I do like the look of that. I'm not sure how it works (I used intellisense to find my version), but I do like it. I shall now scurry away and find out about "type inference".Thanks.
Ross Watson
A: 

Eric Lippert has written an exellent series of posts on his blog about covariance and contravariance. They make my head hurt when reading, but each time I come out slightly smarter. The post which seems to best answer this question is

why-is-covariance-of-value-typed-arrays-inconsistent

I would recommend browsing his blog for related posts.

Hope this helps

Modan
A: 

It's illegal because the compiler does not take it upon itself to assume that what you want is a new array containing oranges that have been converted from apples.

Why doesn't it assume that it can do this when a explicit conversion from apples to oranges exists?

First because explicit means that the code should specifically request it. For example this would still fail:-

anOrange = anApple;

By using the explicit keyword you are saying the a conversion is available but it must be requested explicitly like this:-

anOrange = (Orange)anApple;

Secondly even if implicit keyword is used so that this would be legal:-

anOrange = anApple;

This doesn't imply that the same is true for an entire array of each. Why is that, the designers could see that the element type of the array has an implicit conversion so technically it would be possible to create a new array and perform all the conversions? I don't know you'd have to ask them. At a guess the benefit that this would give is minimal especially as there is a clear one-line alternative that you've already discoverd. The cost of developing, testing, etc (a la Eric Lippert's usual list) would be quite high I would imagine.

AnthonyWJones
Thanks, I actually wrote the example with implicit conversions first (which didn't work) and then thought to try the explicit version - only the code still tried to implicitly convert the array. Ooops.
Ross Watson