Eric
Eric

Reputation: 6016

Why doesn't this C# casting example work?

I'm trying to understand why this cast doesn't work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CastTest {

    class Foo {}

    // I want to use this kind of like a typedef, to avoid writing List<Foo> everywhere.
    class FooList : List<Foo> {}

    class Program {
        static void Main(string[] args) {
            FooList list = (FooList) Program.GetFooList();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList() {
            return new List<Foo>();
        }
    }
}

This generates a runtime error:

InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[CastTest.Foo]' to type 'CastTest.FooList'.

Can anyone explain why this doesn't work, and whether I can get around this somehow?

Upvotes: 5

Views: 794

Answers (7)

Ricky Gummadi
Ricky Gummadi

Reputation: 5222

You cant upcast; but you could create a new instance of FooList and the contents to it

class Foo { }

    // I want to use this kind of like a typedef, to avoid writing List<Foo> everywhere.
    class FooList : List<Foo> {}

    class Program
    {
        static void Main(string[] args)
        {
            FooList l = new FooList();
            l.AddRange(GetFooList().Select(foo => foo));

            Console.ReadLine();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList()
        {
            return new List<Foo>();
        }
    }

Upvotes: 1

eouw0o83hf
eouw0o83hf

Reputation: 9588

You cannot automatically cast from a parent class to a derived class: just because the object you're attempting to cast is a List<Foo> doesn't necessarily make it a FooList.

Check out:

http://social.msdn.microsoft.com/Forums/is/csharplanguage/thread/f7362ba9-48cd-49eb-9c65-d91355a3daee

You could write an operator that converts a List<Foo> to a FooList, perhaps using AddRange to populate the values, such as:

class FooList
{
 public explicit operator FooList(List<Foo> arg)
 {
   FooList result = new FooList();
   result.AddRange(arg);
   return result;
  }
}

Also, it may be better to just use a using alias: http://msdn.microsoft.com/en-us/library/sf0df423(v=vs.80).aspx

using FooList = List<Foo>;

This way you're not actually passing around needless derived classes.

Upvotes: 5

PMontgomery
PMontgomery

Reputation: 424

Because not all List are FooList. For example, I could have:

public class AnotherFooList : List<Foo>
{
    public object AdditionalPropery {get; set; }
}

It too inherits from List but is not the same as a FooList and a FooList is not the same as it.

Upvotes: 2

faester
faester

Reputation: 15076

Since you know that a FooList is in reality just another name for a List<Foo> it may seem strange. But given that a FooList could contain members itself, it becomes apparant that the compiler should not allow the cast. Imagine you at a later time introduce a method (Bar()) on the FooList. This method is not present on List<Foo> and the type system would simply break if the assignment (or cast) was allowed.

Upvotes: 1

user743382
user743382

Reputation:

The closest C# has to a typedef is called a using alias directive:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CastTest {
    using FooList = List<Foo>;

    class Foo {}

    class Program {
        static void Main(string[] args) {
            FooList list = Program.GetFooList();
        }

        // Suppose this is some library method, and i don't have control over the return type
        static List<Foo> GetFooList() {
            return new List<Foo>();
        }
    }
}

What you have creates a derived class FooList, but this creates an alias FooList. It isn't somewhat compatible with List<Foo>, it is List<Foo>.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1499740

This method:

static List<Foo> GetFooList() {
    return new List<Foo>();
}

... doesn't return a FooList. Your code is effectively like this:

FooList list = (FooList) new List<Foo>();

That's simply not going to work. It's as invalid as:

string x = (string) new object();

You wouldn't expect that to work, would you? So why would your FooList version work?

Upvotes: 4

Mattias &#197;slund
Mattias &#197;slund

Reputation: 3907

Just because your FooList is a List doesn't make the List a FooList.

Upvotes: 7

Related Questions