1

I have the following classes:

public class Column
{
    public String Name;
    public String Value;
}

public class ColumnList : List<Column> {}

Then I do:

var cols = (from c in othercolumnlist
            select new Column
            {
               Name = c.Name,
            }).ToList() as ColumnList;

However it returns null. How can I successfully cast into my list?

Ivan-Mark Debono
  • 15,500
  • 29
  • 132
  • 263
  • Do you really have to inherit from `List`?? you may see this question as well http://stackoverflow.com/questions/5376203/inherit-listt – Habib Mar 24 '14 at 14:17
  • Why should [`ToList`](http://msdn.microsoft.com/en-us/library/bb342261%28v=vs.110%29.aspx) from the BCL return an instance of your custom class `ColumnList` (which is what you hope for by applying the `as` operator)? – O. R. Mapper Mar 24 '14 at 14:18

4 Answers4

7

ColumnList is a List<Column> but List<Column> is not a ColumnList. Consider Animal and Dog classes:

public class Animal
{
}

public class Dog: Animal
{
}

What you are trying to do is treat any animal as dog. But not all animals are dogs. There can be Cat or Kangaroo. Thats why as ColumnList returns null.

You need to create new ColumnList instance instead and add columns to it manually:

var columnList = new ColumnList();
columnList.AddRange(othercolumnlist.Select(c => new Column { Name = c.Name }));

If you need to do this often, then you can add static creation method to your ColumnList class:

public static ColumnList CreateFrom(ColumnList other)
{
     var columnList = new ColumnList();
     columnList.AddRange(other.Select(c => new Column { Name = c.Name }));
     return columnList;
}

And use it this way:

var columnList = ColumnList.CreateFrom(otherColumnList);
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
4

What you're trying to do is equivalent to this:

Animal animal = new Animal();
Dog dog = animal as Dog;

One possible solution is to provide a constructor that takes an existing list, and calls the base constructor, such as:

public class ColumnList : List<Column>
{
    public ColumnList(IEnumerable<Column> collection):
         base(collection)
    {
    }
}

And then build your ColumnList from an existing collection.

var collection = othercolumnlist.Select(c => new Column { Name = c.Name });

var columnList = new ColumnList(collection);

You could also provide an extension method to make this easier:

public static class ColumnListExtensions
{
    public static ToColumnList(this IEnumerable<Column> collection)
    {
        return new ColumnList(collection);
    }   
}

var cols = othercolumnlist.Select(c => new Column { Name = c.Name })
           .ToColumnList();
dcastro
  • 66,540
  • 21
  • 145
  • 155
2

What you are trying to do is down-cast List<Column> to ColumnList which assumes that all List<T>'s are ColumnList's - which obviously they aren't.

What you can do however is initialize a new instance of ColumnList by passing the result of your query

new ColumnList().AddRange(othercolumnlist
    .Select(x => new Column { Name = x.Name })
    .ToList());
James
  • 80,725
  • 18
  • 167
  • 237
  • Um, you can't upcast? Sure you can. `(object)"Hello world"` – Lews Therin Mar 24 '14 at 14:29
  • @LewsTherin `string` derives from `object` therefore your example is not upcasting, it's downcasting (which is perfectly legal). Upcasting would be going `var obj = new object(); string str = (string)obj` - and although that will work, `string` is a special type of object and is treated differently by the CLR. – James Mar 24 '14 at 14:40
  • 1
    string derives from object and I am casting from string to object. It is up casting. I am casting up the inheritance chain. – Lews Therin Mar 24 '14 at 14:43
  • @LewsTherin ah wait - I have completely confused the definitions there, my apologies. – James Mar 24 '14 at 14:50
0

You could if you define a copy constructor for your ColumnList type:

public class ColumnList : List<Column>
{
    public ColumnList(IEnumerable<Column> src) : base(src)
    {
    }
}

And you then do:

var cols = new ColumnList(from c in othercolumnlist
            select new Column
            {
               Name = c.Name,
            }).ToList());

Reason being ToList() returns a List and not a ColumnList. It can only ever be a ColumnList if you explicitly instantiate it as one, but never vice-versa.

dcastro
  • 66,540
  • 21
  • 145
  • 155
aevitas
  • 3,753
  • 2
  • 28
  • 39