11

I deserialized json string to List<ClassB> and now I want to cast it to List<ClassA> before I return it from BindModel method. I need casting because the methods expects to get List<ClassA>.

Why I get error while casting? After all, ClassB inherits from ClassA. What should I do?

P.S. this question is extended from this post. In line new DataContractJsonSerializer(typeof(List<ClassB>)); instead of List<ClassB> the type will be constructed at runtime.

    public override object BindModel(...)
    {
          var serializer = new DataContractJsonSerializer(typeof(List<ClassB>));
          MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes("[{\"id\":\"1\",\"name\":\"name\"}]"));
          var list = serializer.ReadObject(ms);

          return (List<ClassA>)list;
    }

    [KnownType(typeof(ClassA))]
    [DataContract]
    public class ClassA
    {
        public ClassA();
    }

    [KnownType(typeof(ClassB))]       
    [DataContract]
    public class ClassB : ClassA
    {
        [DataMember(Name = "id")]
        public int Id { get; set; }
        [DataMember(Name = "name")]
        public string CategoryName { get; set; }
    }
Community
  • 1
  • 1
theateist
  • 13,879
  • 17
  • 69
  • 109

5 Answers5

12

You can use

 Cast<T>()

For example:

 List<A> listOfA = new List<B>().Cast<A>();

This in fact is inferior to Linq and is implemented on IEnumerable rather than IEnumerable<T> but still is useful. It is not efficient since as I said, it tries to cast it.

Remember List does not allow for covariance which is a nuisance. It is preferable to use IEnumerable<T> as interface rather than List.

You can say:

 IEnumerable<B> listOfB = new List<B>();
 IEnumerable<A> listOfA = listOfB; // no casting required
Aliostad
  • 80,612
  • 21
  • 160
  • 208
6

You can use the Cast Method.

return list.Cast<ClassB>();

Have a look at this question about Co and Contra Variance

Community
  • 1
  • 1
Ray
  • 45,695
  • 27
  • 126
  • 169
  • I'll read the post, but `serializer.ReadObject` return `object` so I cannot use `Cast` and `ClassB` was just example as I said in Postscript(P.S.), any suggastions? – theateist Oct 31 '11 at 15:59
  • Cast returns IEnumerable, but the OP needs a List. – phoog Oct 31 '11 at 19:03
  • @theateist if serializer.ReadObject retuns an object whose runtime type is List then you can do this (but it returns an enumerable, not a list): `((List)obj).Cast()`. Even though you can't cast a `List` to `List`, you *can* cast a reference to `List` from `object` to `List` – phoog Oct 31 '11 at 19:12
2

I would cast the lists like this:

var listB = GetListOfB();  // returns List<B>
var listA = listB.Select(q => (A)q).ToList();

Will that work for you?

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
1

You can't cast a List of SubType to a List of SuperType. Suppose I have a list of Tortoises, and I was able to cast it to a list of Animals. Then I could add a lion, to a list of Tortoises, but a Lion isn't of the correct type.

With enumerables, you can do this, however. The previous posters are quite correct in saying that you can cast a List of SubType to an IEnumerable of SuperType. In fact, in C# 4, and IEnumerable SubType is an IEnumerable of SuperType. This is because the generic parameter is specified as an out parameter.

Adam Brown
  • 11
  • 1
1

To create a List<BaseClass> from a List<DerivedClass>, a cast will not suffice, as other answers have noted. You need to construct a new list.

I'm not particularly happy with the code that other answers have suggested, though, so I'm offering my own solution. I would do it this way:

var baseClassList = new List<BaseClass>(derivedClassList.Cast<BaseClass>());

Some might prefer this:

var baseClassList = derivedClassList.Cast<BaseClass>().ToList();

I prefer the first one because it makes it easy to change the type from List<T> to any other collection with a constructor that takes an IEnumerable<T>.

phoog
  • 42,068
  • 6
  • 79
  • 117