1

I am trying to cast a list of objects within a consturctor for a derive class IntersectionPath as follows.

    public class IntersectionPath : Path<IntersectionSegment>, IEnumerable
    {          

        //Constructors
        public IntersectionPath() : base() {  Verts = null; }

        public IntersectionPath(List<Intersection> inVerts, List<Segment<Node>> inEdges) : base() 
        {
            this.Segments = (List<IntersectionSegment>) inEdges;
        }

    }

Segments is defined in the generic base class Path

    public class Path<T> : IEnumerable<T> where T : Segment<Node>
    {   
        //public properties
        public List<Direction> Directions {get; set; }
        public List<T> Segments  {  get; set; }
    }

I have defined an explicit operator for the cast within the IntersectionSegment class (see below and so am unclear as to why this won't compile. I have an error message for the casting within the IntersectionPath constructor.

public class IntersectionSegment : Segment<Intersection>
{           
    //curves which intersect the primary curve at I0(Start Node) and I1(End Node)
    public Curve C0 { get; set; }
    public Curve C1 { get; set; }

    public IntersectionSegment():base() {}

    public IntersectionSegment(Intersection n0, Intersection n1):base(n0,n1){}

    public static explicit operator IntersectionSegment(Segment<Node> s)
    {
        if ((s.Start is Intersection) && (s.End is Intersection))
        {
            return new IntersectionSegment(s.Start as Intersection,s.End as Intersection);
        }
        else return null;
    }

    public static explicit operator List<IntersectionSegment>(List<Segment<Node>> ls)
    {
        List<IntersectionSegment> lsout = new List<IntersectionSegment>();
        foreach (Segment<Node> s in ls)
        {
            if ((s.Start is Intersection) && (s.End is Intersection))
            {
                lsout.Add(new IntersectionSegment(s.Start as Intersection,s.End as Intersection));
            }
            else return null;
        }
        return lsout;
    }

Segment is defined as:

public class Segment <T> : Shape where T : Node
{
    //generic properties
    public T Start { get; set; }
    public T End { get; set; }

 }
gwizardry
  • 501
  • 1
  • 6
  • 19

3 Answers3

7

List<InteractionSegment> is not the same as InteractionSegment. Casting a list of one type to a list of another type won't cast each item.
You need to do something like this:

this.Segments = inEdges.Select(x => (InteractionSegment)x).ToList();

This uses LINQ to Objects to cast each object in inEdges to an InteractionSegment object and puts the result back into a list that is then assigned to this.Segments.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
  • OK thanks. Not sure i understand the reasoning though, as I thought the point of the explicit cast i've written was to overcome this. – gwizardry Mar 27 '12 at 14:28
  • You have written an explicit cast between `InteractionSegment` and `Segment` and **not** between `List` and `List>`. Your explicit cast is still necessary for and used by my code. – Daniel Hilgarth Mar 27 '12 at 14:29
  • I thought i'd written BOTH - please check. – gwizardry Mar 27 '12 at 14:32
  • @gwizardry: You are right. I missed that. However, this cast is not being used because it is an invalid operator declaration according to the specification. In [the MSDN](http://msdn.microsoft.com/en-us/library/s53ehcz3%28v=vs.71%29.aspx) it is stated that "at least one of op-type and op-type2 must be the enclosing type ". The enclosing type is `IntersectionSegment`, op-type is `List` and op-type2 is `List>` – Daniel Hilgarth Mar 27 '12 at 14:37
  • Ah ok thanks. I did wonder that when i coded it, but as it was not picked up as the actual compiler error message, i didn't reconsider it. – gwizardry Mar 27 '12 at 14:40
5

Let's look at a far less confusing example.

class Animal {}
class Giraffe : Animal {}
class Tiger : Animal {}
...
List<Giraffe> giraffes = new List<Giraffe>();
List<Animal> animals = (List<Animal>) giraffes; // illegal

Your question is I believe "why is the cast on the last line illegal?"

Let's suppose it was legal. Now we add another line:

animals.Add(new Tiger());

You can add a tiger to a list of animals, right? But that list of animals is actually a list of giraffes. The cast does not copy the list, it says "I want to treat this object as being of this type". But since doing so would allow you to do crazy things like putting a tiger into a list of giraffes, we make the cast illegal.

Your situation is just a much more complicated version of the same situation.

This question gets asked almost every day on StackOverflow. Look for "covariance and contravariance" and you will find dozens of examples.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I see that animals is just a variable label that accesses the same object as giraffes that was newed in the line above. i.e. its just a reference. If this is so, what happens to a new object created in a legitimate explicit cast (say if we created one for Animal instead of List)? – gwizardry Mar 27 '12 at 15:01
  • @gwizardry: if you want to make a user-defined explicit conversion operator from `Animal` to `List`, that's fine. Such a conversion would convert from any `Animal` to `List`, so you could convert a `Giraffe` to a `List`. But the result would not be a `List`. – Eric Lippert Mar 27 '12 at 15:10
  • No i meant Giraffe to Animal. But my query was more about the logic if we're returning a new object (such as i do in my casting operators above). Does that change your description of "treat this object as being of this type" – gwizardry Mar 27 '12 at 15:35
  • 1
    @gwizardry: A cast can mean both "treat this object that you think is one type as being of this other type, without changing it" and "obtain an object of the desired type given an expression that is not of that type". You will notice that those two things are *opposites*, which on the one hand is pretty neat, and on the other hand is confusing as all heck. See http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx for more thoughts. – Eric Lippert Mar 27 '12 at 15:48
  • @gwizardry: Note also that you cannot make a user-defined conversion from Giraffe to Animal (or Animal to Giraffe) because that would be redefining an existing conversion. That's illegal in C#. – Eric Lippert Mar 27 '12 at 15:49
  • Thanks i'll take a look - you're right I had assumed that casting performed the same 'base' operation, so would be good to clear that up – gwizardry Mar 28 '12 at 08:15
2

It doesn't work simply because a List<Segment<Node>> is not a List<IntersectionSegment>. If you want to create the later your can use Cast() to explicitly cast each item in the list to the type you want:

this.Segments = inEdges.Cast<IntersectionSegment>().ToList();
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335