2009-08-12 27 views
24

¿Por qué no puedo emitir List<ObjBase> como List<Obj>? ¿Por qué el no sigue a trabajar:C# Casting a List <ObjBase> como List <Obj>

internal class ObjBase 
    { 
    } 

internal class Obj : ObjBase 
    { 
    } 

internal class ObjManager 
{ 
    internal List<Obj> returnStuff() 
    { 
     return getSomeStuff() as List<Obj>; 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 

} 

En lugar de eso tienen que hacer esto:

internal class ObjBase 
    { 
    } 

internal class Obj : ObjBase 
    { 
    } 

internal class ObjManager 
{ 
    internal List<Obj> returnStuff() 
    { 
     List<ObjBase> returnedList = getSomeStuff(); 
     List<Obj> listToReturn = new List<Obj>(returnedList.Count); 
     foreach (ObjBase currentBaseObject in returnedList) 
     { 
      listToReturn.Add(currentBaseObject as Obj); 
     } 
     return listToReturn; 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 
} 

me sale el siguiente error en Visual Studio 2008 (abreviado para facilitar la lectura):

No se puede tipo de conversión 'Lista' a 'Lista' a través de una conversión de referencia, conversión de boxeo, conversión de unboxing, conversión de envoltura o conversión de tipo nulo

Gracias.

+0

-1: Dup de muchas preguntas aquí, incluyendo http://stackoverflow.com/questions/1263489/how- do-i-upcast-a-collection-of-base-class-in-c/1263514 # 1263514 y los que se citan en la respuesta a continuación –

+0

Posible duplicado de [.NET Casting Generic List] (http://stackoverflow.com/questions/674715/net-casting-generic-list) –

Respuesta

1

Creo que estás malinterpretando el reparto que intentas hacer. Está pensando que está cambiando el tipo de objeto que está almacenado en la lista, donde realmente está tratando de cambiar el tipo de la lista. Más bien tiene sentido que no pueda cambiar la lista como ya la ha llenado.

Puede verlo como una lista de una clase base y luego lanzarlo cuando está procesando los elementos de la lista, ese sería mi enfoque.

¿Cuál es el propósito de este intento de lanzamiento?

0

LINQ tiene un método ConvertAll. algo así como

list.ConvertAll<Obj>(objBase => objbase.ConvertTo(obj)); 

No estoy seguro de qué más sugerir. Supongo que ObjBase es la clase base, y si todos los objetos ObjBase son objetos Obj, no estoy seguro de por qué tendrías los dos objetos en primer lugar. Tal vez estoy fuera de lugar.

Editar: la lista. El método de prueba funcionaría mejor que el anterior, suponiendo que se pueden fusionar entre sí. Olvidé eso hasta que leí las otras respuestas.

9

que sólo puedo describir el "problema" de una vista de Java, pero por lo poco que sé este aspecto es el mismo tanto en C# y Java:

Un List<ObjBase> no es un List<Obj>, porque podría contener un objeto ObjBase que no es un objeto Obj.

La otra manera alrededor de un List<Obj> puede no será echado a un List<ObjBase> porque el primero garantías para aceptar una llamada con un argumento Add()ObjBase, que este último no aceptará!

Entonces, para resumir: a pesar de que un Obj is-a ObjBase un List<Obj> no es un List<ObjBase>.

+0

Esta es la descripción más simple del problema genérico covariante que he leído. Bonito. –

1

C# actualmente no es compatible con la varianza para los tipos genéricos. Por lo que he leído, esto cambiará en 4.0.

Consulte here para obtener más información sobre la varianza en genéricos.

32

Puede usar los métodos de extensión Cast y ToList de System.Linq para tener esto en una línea.

En lugar de

internal List<Obj> returnStuff() 
{ 
    return getSomeStuff() as List<Obj>; 
} 

hacer esto:

internal List<Obj> returnStuff() 
{ 
    return getSomeStuff().Cast<Obj>().ToList(); 
} 
0

Este es un gran dolor en C# - así es como se diseñaron los genéricos. List no extiende List, es simplemente un tipo completamente diferente. No puede transmitirlos o asignarlos entre sí de ninguna manera, su única opción es copiar una lista en la otra.

0

Lazarus:
pensé que el compilador se daría cuenta de que quería acciones realizadas sobre los objetos de la lista y no es que yo estaba tratando de echar la propia lista.

Algunos más información:

public abstract class ObjBase 
    { 
    } 

internal interface IDatabaseObject 
    { 
    } 

public class Obj : ObjBase, IDatabaseObject 
    { 
    } 


internal interface IDatabaseObjectManager 
    { 
     List<ObjBase> getSomeStuff(); 
    } 

public class ObjManager : IObjManager 
{ 
    public List<Obj> returnStuff() 
    { 
     return getSomeStuff().Cast <Customer>().ToList<Customer>(); 
    } 

    private List<ObjBase> getSomeStuff() 
    { 
     return new List<ObjBase>(); 
    } 
} 

Ahora código de cliente fuera de este DLL puede ir: ObjManager objM = new ObjManager(); List listOB = objM.returnStuff(); Voy a crear varios tipos Obj y ObjManager para esta parte (O/RM) de la aplicación.

(bloque de comentario Darn quedó sin caracteres! :-)

1

list.ConvertAll parece tentador, pero tiene una gran desventaja: creará una lista completamente nueva. Esto afectará el rendimiento y el uso de memoria, especialmente para listas grandes.

Con un poco más de esfuerzo puede crear una clase de lista contenedora que conserve la lista original como referencia interna y convierta los elementos solo cuando se utilicen.

Uso:

var x = new List<ObjBase>(); 
var y = x.CastList<ObjBase, Obj>(); // y is now an IList<Obj> 

código para agregar a la librería:

public static class Extensions 
{ 
    public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list) 
    { 
     return new CastedList<TTo, TFrom>(list); 
    } 
} 

public class CastedList<TTo, TFrom> : IList<TTo> 
{ 
    public IList<TFrom> BaseList; 

    public CastedList(IList<TFrom> baseList) 
    { 
     BaseList = baseList; 
    } 

    // IEnumerable 
    IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); } 

    // IEnumerable<> 
    public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); } 

    // ICollection 
    public int Count { get { return BaseList.Count; } } 
    public bool IsReadOnly { get { return BaseList.IsReadOnly; } } 
    public void Add(TTo item) { BaseList.Add((TFrom)(object)item); } 
    public void Clear() { BaseList.Clear(); } 
    public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); } 
    public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); } 
    public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); } 

    // IList 
    public TTo this[int index] 
    { 
     get { return (TTo)(object)BaseList[index]; } 
     set { BaseList[index] = (TFrom)(object)value; } 
    } 

    public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); } 
    public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); } 
    public void RemoveAt(int index) { BaseList.RemoveAt(index); } 
} 

public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo> 
{ 
    public IEnumerator<TFrom> BaseEnumerator; 

    public CastedEnumerator(IEnumerator<TFrom> baseEnumerator) 
    { 
     BaseEnumerator = baseEnumerator; 
    } 

    // IDisposable 
    public void Dispose() { BaseEnumerator.Dispose(); } 

    // IEnumerator 
    object IEnumerator.Current { get { return BaseEnumerator.Current; } } 
    public bool MoveNext() { return BaseEnumerator.MoveNext(); } 
    public void Reset() { BaseEnumerator.Reset(); } 

    // IEnumerator<> 
    public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } } 
}