2011-10-31 16 views
10

I deserialized cadena json a List<ClassB> y ahora quiero convertirlo a List<ClassA> antes de devolverlo desde BindModel método. Necesito el casting porque los métodos esperan obtener List<ClassA>.Cómo agregar la lista <ClassB> a la lista <ClassA> cuando ClassB hereda de ClassA?

¿Por qué obtengo un error durante la conversión? Después de todo, ClassB hereda de ClassA. ¿Que debería hacer?

P.S. esta pregunta se extiende desde this post. En la línea new DataContractJsonSerializer(typeof(List<ClassB>)); en lugar de List<ClassB>, el tipo se construirá en tiempo de ejecución.

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; } 
    } 
+0

¿Estás seguro de que estás utilizando .NET 4.0? Lo que quiere se llama 'covarianza' y ya debe ser compatible. – nothrow

+1

La lista no es compatible con la covarianza. – Aliostad

Respuesta

10

Usted puede utilizar

Cast<T>() 

Por ejemplo:

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

Esto de hecho es inferior a Linq y se implementa en IEnumerable en lugar de IEnumerable<T>, pero sigue siendo útil. Es no eficiente ya que como he dicho, intenta lanzarlo.

Recuerde La lista no permite covarianza que es una molestia. Es preferible usar IEnumerable<T> como interfaz en lugar de Lista.

Se puede decir:

IEnumerable<B> listOfB = new List<B>(); 
IEnumerable<A> listOfA = listOfB; // no casting required 
+1

Funcionó, cambié' List' to ' IEnumerable', ya que no necesito agregar elementos más adelante :) – theateist

+0

¿Cómo es esto "inferior a Linq"? It * is * Linq! – phoog

+0

¡No lo es! Como dije, está implementado por 'IEnumerable' que no es Linq. Pero 'Cast ' lo convierte en Linq. – Aliostad

6

Puede usar el Cast Method.

return list.Cast<ClassB>();

Tenga una mirada en this question sobre Co y Contra Varianza

+0

Leeré la publicación, pero 'serializer.ReadObject' devolverá' object', así que no puedo usar 'Cast ' y 'ClassB' fue solo un ejemplo, como dije en Postscript (P.S), ¿alguna sugerencia? – theateist

+0

Cast devuelve IEnumerable , pero el OP necesita una lista . – phoog

+0

@theateist if serializer.ReadObject vuelve a colocar un objeto cuyo tipo de tiempo de ejecución es List y luego puede hacer esto (pero devuelve un enumerable, no una lista): '((List ) obj) .Cast ()'. Aunque no puede convertir una 'List ' en 'List ', * puede * hacer una referencia a 'List ' from 'object' to' List ' – phoog

2

me habría echado las listas de la siguiente manera:

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

que el trabajo para usted?

+0

+1, pero ¿por qué usar '.Select (q => (A) q)' en lugar de '.Cast ()'? – phoog

1

No se puede emitir una Lista de Subtipo a una lista de supertipo. Supongamos que tengo una lista de tortugas, y pude lanzarla a una lista de animales. Entonces podría agregar un león, a una lista de Tortugas, pero un León no es del tipo correcto.

Con los enumerables, puede hacer esto, sin embargo. Los carteles anteriores son bastante correctos al decir que puede lanzar una lista de Subtipo a un IEnumerable de SuperType. De hecho, en C# 4 e IEnumerable SubType es un IEnumerable de SuperType. Esto se debe a que el parámetro genérico se especifica como un parámetro de salida.

1

Para crear un List<BaseClass> desde un List<DerivedClass>, un lanzamiento no será suficiente, como han notado otras respuestas. Necesitas construir una nueva lista.

No estoy particularmente contento con el código que otras respuestas han sugerido, así que estoy ofreciendo mi propia solución. Me gustaría hacerlo de esta manera:

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

Algunos podrían preferir este:

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

Yo prefiero el primero, ya que hace que sea fácil cambiar el tipo de List<T> a cualquier otra colección con un constructor que toma un IEnumerable<T>.

Cuestiones relacionadas