2011-03-08 18 views
6

¿Cómo puedo downcast una lista de objetos para que cada uno de los objetos en la lista se downcast a un objeto de una clase derivada?Downcasting una lista de objetos en C#

Este es el escenario.

que tienen una clase base con un List de elementos de base, y dos clases que heredan de ella:

public class BaseClass 
{ 
    public List<BaseItem> items; 
    protected BaseClass() 
    { 
     // some code to build list of items 
    } 
} 
public class DerivedClass : BaseClass 
{ 
    public DerivedClass : base() {} 
} 
public class AnotherDerivedClass : BaseClass 
{ 
    public AnotherDerivedClass : base() {} 
} 

public class BaseItem {} 
public class DerivedItem : BaseItem {} 
public class AnotherDerivedItem : BaseItem {} 

La idea es no tener que duplicar el código necesario para construir la lista de elementos. El BaseItem tiene todas las cosas básicas que necesito, y siempre puedo enviar BaseItem a uno de los artículos derivados.

El problema surge cuando tengo una lista de ellos. El List de BaseItem se declara en el BaseClass porque todas las clases derivadas deben tenerlo. Pero al acceder a él en tiempo de ejecución, parece que no puedo bajar a la clase derivada.

+0

Probablemente debería estar buscando en a las interfaces. –

+0

O las palabras clave AS y IS. – ThatBlairGuy

+0

Polimorfismo estándar de pantano: métodos virtuales en la clase base, que anula en las clases derivadas. No se requiere downcasting porque utiliza el tipo de clase base para llamar a los métodos anulados. – Polyfun

Respuesta

5

mediante LINQ:

var baseList = new List<BaseClass>(); 
    var derivedList = baseList.Cast<DerivedClass>(); 

Nota: Tener que abatido por lo general es un 'olor' e indica que la jerarquía de herencia está mal, o mal aplicadas. La idea de tener una clase base es que puede tratar todas las subclases como superclase sin tener que bajar a tipos de subclase individuales.

En lugar de Cast es posible que desee utilizar OfType para 'pescar' ciertas clases derivadas de una colección de superclases. Pero, de nuevo, no debería haber necesidad de hacer eso.

Pregúntese, ¿por qué necesita tener una subclase? ¿Tal vez necesite mover alguna funcionalidad a la clase base?

+0

Éste hace exactamente lo que estaba pidiendo. Pero las otras respuestas y comentarios me hacen preguntarme si tal vez podría evitar hacer este tipo de cosas por completo. – Farinha

+0

@Farinha - Se agregó una nota –

+0

Y podría jurar que esta solución funcionó anoche ... y ya no ... – Farinha

5

Creo que lo que quiere hacer es genéricos de uso:

public class BaseClass<T> where T: BaseItem, new() 
{ 
    public List<T> items; 
    protected BaseClass() 
    { 
     items = new List<T>(); 
     T item = new T(); 
     item.SomePropertyOfBaseItem=something; 
     items.Add(item); 
    } 
} 

public class DerivedClass: BaseClass<DerivedItem> 

Esto hará que el items en DerivedClass ser List<DerivedItem>. El where exige que solo se puedan usar los tipos que se derivan de BaseItem.

edit: "downcasting", enviar un tipo a un tipo derivado, no es realmente lo que estás tratando de hacer aquí. Su intención es que los objetos de la lista derivada utilicen un tipo de elemento derivado específico por diseño, y presumiblemente desea almacenar objetos instanciados del tipo derivado en su clase de lista derivada.

Por lo tanto, esto podría funcionar bien sin usar genéricos: el List<BaseItem> es perfectamente capaz de almacenar cualquier elemento que derive de BaseItem. Sin embargo, tendría que hacer referencia a estos objetos de la lista utilizando el casting (como se describe en las otras respuestas) para acceder a las propiedades derivadas. Pero eso es simplemente "lanzar" un objeto a su verdadero tipo. Generics le brinda una manera de proporcionar acceso fuertemente tipado a estos objetos directamente.

Básicamente, almacenar un objeto en un contenedor que es una superclase del objeto no cambia nada en el objeto; solo cambia la forma en que su código puede referirse a él, haciendo que parezca ser del tipo más simple de de lo que se deriva

+0

El problema aquí es que es útil tener el atributo de elementos en la BaseClass, porque de lo contrario no podría tener el constructor de la BaseClass construyéndolo. – Farinha

+0

¿Por qué no? ¿Qué haría su constructor con una 'List ' que no podría hacer tan fácilmente con una 'List '? El compilador realmente tratará a 'T' tal como' BaseItem' en su clase base debido a la cláusula 'where'. –

+0

Btw: los genéricos son, posiblemente, la adición más útil a C# desde 1.0. Lo sé, tendré un poco de desacuerdo con las personas que les gusta linq, o quizás '='. Pero si bien puede crear código que haga lo mismo usando casting, usar genéricos en su lugar hará que su código sea más legible, comprensible y resistente a los errores, al permitirle escribir fuertemente los miembros internos y los valores devueltos. –

0
public class Zoo 
{ 
    public List<Animal> items; 
    protected BaseClass() 
    {   // some code to build list of items  } 
} 

public class PettingZoo : Zoo 
{ 
    public PettingZoo : base() {} 
} 

public class CatComplex : Zoo 
{ 
    public CatComplex : base() {} 
} 

public class Animal {} 
public class Sheep : Animal {} 
public class Tiger : Animal {} 

...

PettingZoo myZoo = new PettingZoo(); 
myZoo.items.Add(new Tiger()); 

PettingZoo no es intercambiable con zoológico, como PettingZoo debe restringir los tipos de animales. Como tal, este diseño falla the Liskov substitution principle.

0

También puede utilizar LINQ para iterar a través de los elementos de la clase base y abatido cada elemento, así:

var baseList = new List<BaseClass>(); 
var derivedList = new List<DerivedClass>(); 
baseList.ForEach(v => derivedList.Add((DerivedClass)v)); 

Si es necesario comprobar el tipo derivado de cada elemento antes de la fundición:

var baseList = new List<BaseClass>(); 
var derivedList = new List<DerivedClass>(); 
baseList.OfType<DerivedClass>().ToList().ForEach(v => derivedList.Add(v)); 

Para obtener más información, echar un vistazo a los siguientes artículos:

Cuestiones relacionadas