Se buscan sugerencias con Listas o Enumeradores de T al heredar de clases genéricas

StackOverflow https://stackoverflow.com/questions/53395

  •  09-06-2019
  •  | 
  •  

Pregunta

Sé que la respuesta no será sencilla y ya uso un par de trucos (creo que feos).Simplemente estoy buscando algunas respuestas elegantes.

Clase abstracta:

public interface IOtherObjects;

public abstract class MyObjects<T> where T : IOtherObjects
{
   ...

   public List<T> ToList()
   {
       ...
   }
}

Niños:

public class MyObjectsA : MyObjects<OtherObjectA> //(where OtherObjectA implements IOtherObjects)
{


}

public class MyObjectsB : MyObjects<OtherObjectB> //(where OtherObjectB implements IOtherObjects)
{


}

¿Es posible recorrer una colección de MisObjetos (u otra agrupación similar, genérica o no) para luego utilizarla? Listar método de la Mis objetos clase base, ya que no sabemos específicamente el tipo de T en este momento.

EDITAREn cuanto a ejemplos específicos, siempre que surgió esto, lo pensé por un tiempo y en su lugar hice algo diferente, por lo que no hay ningún requisito actual.pero como ha surgido con bastante frecuencia, pensé en publicarlo.

EDITAR@Sara, no es el tipo específico de colección lo que me importa, podría ser una Lista, pero aun así el método ToList de cada instancia es relativamente inutilizable, sin un tipo anónimo)

@aku, es cierto, y esta pregunta puede ser relativamente hipotética; sin embargo, sería muy útil poder recuperar y trabajar con una lista de T de objetos, sabiendo solo su tipo base.Hacer que ToList devuelva una Lista de tipo base ha sido una de mis soluciones

EDITAR @ todo:Hasta ahora, este ha sido el tipo de discusión que esperaba, aunque confirma en gran medida todo lo que sospechaba.Gracias a todos hasta ahora, pero cualquier otra persona puede contribuir.

EDITAR@Rob, Sí, funciona para un tipo definido, pero no cuando el tipo solo se conoce como Lista de IOtherObjects.

@Robar De nuevo Gracias.Por lo general, esa ha sido mi solución complicada (sin faltarle el respeto :)).O eso o usar la función ConvertAll para Downcast a través de un delegado.Gracias por tomarse el tiempo para comprender el problema.

EDICIÓN DE CALIFICACIÓN por si he sido un poco confuso

Para ser más precisos (es posible que haya dejado que mi última implementación de esto se vuelva demasiado compleja):

digamos que tengo 2 tipos de objetos, B y C que heredan del objeto A.

Se han presentado muchos escenarios donde, desde una Lista de B o una Lista de C, o en otros casos una Lista de cualquiera de las dos, pero no sé cuál, si estoy en una clase base, he necesitado una Lista de menos específica. A.

El ejemplo anterior fue un ejemplo diluido de la Lista de menos específicos La última encarnación del problema.

Por lo general, se ha presentado solo, mientras pienso en posibles escenarios que limitan la cantidad de código que necesita escribirse y parecen un poco más elegantes que otras opciones.Realmente quería una discusión sobre posibilidades y otros puntos de vista, que más o menos he entendido.Me sorprende que nadie haya mencionado ConvertAll() hasta ahora, ya que es otra solución alternativa que he usado, pero demasiado detallada para los escenarios que nos ocupan.

@Robar Una vez más y sara

Gracias, sin embargo, siento que entiendo los genéricos en todo su esplendor en contexto estático y entendí los problemas en juego aquí.

El diseño real de nuestro sistema y el uso de genéricos (y puedo decir esto sin un toque de parcialidad, ya que yo solo fui uno de los participantes en el diseño) se ha hecho bien.Fue cuando estuve trabajando con la API principal, encontré situaciones en las que estaba en el ámbito equivocado para hacer algo simplemente, en lugar de eso tuve que lidiar con ellas de manera un poco menos elegante de lo que me gustaría (tratando de ser inteligente o tal vez perezoso: aceptaré cualquiera de esas etiquetas).

Mi disgusto por lo que denominé un cludge es en gran medida que necesitamos hacer un bucle a través de nuestro conjunto de registros simplemente para convertir los objetos a su valor base, lo que puede afectar el rendimiento.

Supongo que me preguntaba si alguien más se había encontrado con esto antes en su codificación, y si alguien había sido más inteligente, o al menos más elegante, que yo al lidiar con eso.

¿Fue útil?

Solución

Si usted tiene

class B : A
class C : A

Y tu tienes

List<B> listB;
List<C> listC;

que desea tratar como una Lista del tipo padre

Entonces deberías usar

List<A> listA = listB.Cast<A>().Concat(listC.Cast<A>()).ToList()

Otros consejos

¿Por qué tienes una colección de MyObjects?¿Existe alguna razón específica por la que no tienes una Lista?

En su caso, MyObjectsA y MyObjectsB no tienen un predecesor común.La clase genérica es una plantilla para diferente las clases no son una clase base común.Si desea tener propiedades comunes en diferentes clases, utilice interfaces.no puedes llamar Listar en un bucle porque tiene una firma diferente en diferentes clases.Puedes crear ToList que devuelva objetos en lugar de un tipo específico.

Probablemente todavía puedas acceder al método ToList(), pero como no estás seguro del tipo, ¿no funcionará?

foreach(var myObject in myObjectsList)
    foreach(var obj in myObject.ToList())
        //do something

Por supuesto, esto sólo funcionará en C# 3.0.

Tenga en cuenta que el uso de var es simplemente para eliminar el requisito de saber qué tipo contienen las listas;a diferencia de los comentarios de Frank de que tengo la ilusión de que var hará que la escritura sea dinámica.

Bien, estoy confundido, el siguiente código funciona bien para mí (¡la curiosidad se apoderó de mí!):

// Original Code Snipped for Brevity - See Edit History if Req'd

¿O me he perdido algo?

Actualización tras la respuesta del OP

OK, ahora estoy realmente confundido.Lo que estás diciendo es que quieres obtener una Lista de mecanografiado ¿Valores de una lista genérica/abstracta?(Por lo tanto, las clases secundarias se vuelven irrelevantes).

No puede devolver una lista escrita si los tipos son hijos/implementadores de interfaz: ¡no coinciden!Por supuesto, puede obtener una Lista de elementos que son de un tipo específico de la Lista abstracta de esta manera:

    public List<OfType> TypedList<OfType>() where OfType : IOtherObjects
    {
        List<OfType> rtn = new List<OfType>();

        foreach (IOtherObjects o in _objects)
        {
            Type objType = o.GetType();
            Type reqType = typeof(OfType);

            if (objType == reqType)
                rtn.Add((OfType)o);
        }

        return rtn;
    }

Si todavía estoy fuera de lugar, ¿podría reformular su pregunta?(No parece que sea el único que no está seguro de hacia dónde se dirige).Estoy tratando de establecer si hay algún malentendido sobre los genéricos por su parte.

Otra actualización :D

Bien, entonces parece que quieres/necesitas la opción de obtener la Lista escrita, o la lista base, ¿no?

Esto haría que su clase abstracta se viera así: puede usar ToList para obtener el tipo concreto, o ToBaseList() para obtener una Lista del tipo de interfaz.Esto debería funcionar en cualquier escenario que tenga.¿Eso ayuda?

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<T> ToList()
    {
        return _objects;
    }

    public List<IOtherObjects> ToBaseList()
    {
        List<IOtherObjects> rtn = new List<IOtherObjects>();
        foreach (IOtherObjects o in _objects)
        {
            rtn.Add(o);
        }
        return rtn;
    }
}

Actualización #3

En realidad, no es una solución alternativa "torpe" (sin faltar el respeto): esa es la única forma de hacerlo.Creo que el problema más importante aquí es un problema de diseño/asimilar.Dijiste que tenías un problema, este código lo resuelve.Pero si esperabas hacer algo como:

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<IOtherObjects> Objects
    { get { return _objects; } }
}
#warning This won't compile, its for demo's sake.

Y poder elegir los tipos que surgen de él, ¿de qué otra manera? podría ¡¿hazlo tu?!¡Tengo la sensación de que no entiendes realmente cuál es el objetivo de los genéricos y estás intentando que hagan algo para lo que no están diseñados!

Recientemente encontré el

List<A>.Cast<B>().ToList<B>()

patrón.

Hace exactamente lo que estaba buscando,

Los genéricos se utilizan para comprobaciones de tipos de tiempo estáticos. no despacho en tiempo de ejecución.Utilice herencia/interfaces para el envío en tiempo de ejecución, utilice genéricos para garantías de tipo en tiempo de compilación.

interface IMyObjects : IEnumerable<IOtherObjects> {}
abstract class MyObjects<T> : IMyObjects where T : IOtherObjects {}

IEnumerable<IMyObjects> objs = ...;
foreach (IMyObjects mo in objs) {
    foreach (IOtherObjects oo in mo) {
        Console.WriteLine(oo);
    }
}

(Obviamente, prefiero los enumerables a las listas).

O Simplemente use un lenguaje dinámico adecuado como VB.:-)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top