2012-05-16 10 views
5

Tengo una clase de 3 listas vinculadas diferentes (para guardar las entidades en un juego en el que estoy trabajando). Las listas son todos objetos con el mismo tipo base, pero los mantengo separados por razones de procesamiento. Tenga en cuenta que IEntity, IObject e IUndead heredaron de IEntity.C#, forma genérica para acceder a diferentes listas dentro de una clase

public class EntityBucket 
{ 
    public LinkedList<IEntity> undeadEntities; 
    public LinkedList<IEntity> objects; 
    public LinkedList<IEntity> livingEntities; 

    public EntityBucket() 
    { 
     undeadEntities = new LinkedList<IEntity>(); 
     objects = new LinkedList<IEntity>(); 
     livingEntities = new LinkedList<IEntity>(); 
    } 

    public LinkedList<IEntity> GetList(IObject e) 
    { 
     return objects; 
    } 

    public LinkedList<IEntity> GetList(IUndead e) 
    { 
     return undeadEntities; 
    } 

    public LinkedList<IEntity> GetList(ILiving e) 
    { 
     return livingEntities; 
    } 

} 

tengo 3 métodos para recuperar cada una de las listas, en la actualidad en función de sus parámetros. El hecho de que haya 3 está bien, ya que sé que cada lista requerirá de algún modo su propio acceso. Sin embargo, pasar un objeto instanciado no es ideal, ya que es posible que desee recuperar una lista en algún lado sin tener a mano un objeto de tipo similar. Tenga en cuenta que el objeto aquí ni siquiera se utiliza en los métodos GetList, solo están ahí para determinar qué versión usar. Este es un ejemplo en el que tengo una instancia de objeto que nos ocupa:

public void Delete(IUndead e, World world) 
{ 

    ..... 
    LinkedList<IEntity> list = buckets[k].GetList(e); 
    ..... 
} 

no me gusta esta implementación actual, ya que no siempre se puede tener un objeto instanciado a mano (al representar las entidades, por ejemplo). Estaba pensando en hacerlo genéricamente, pero no estoy seguro si esto es posible con lo que quiero hacer. Con esto también necesito 3 métodos Delete (y 3 de cualquier otro, como add, etc.), uno para cada tipo, IUndead, IObject y ILiving. Siento que esta no es la forma correcta de hacerlo.

Voy a publicar lo que he tratado de hacer hasta ahora bajo petición, pero mis genéricos son bastante malos y creo que sería un desperdicio para cualquiera leer esto también.

Finalmente, el rendimiento es muy importante. No estoy optimizando prematuramente, estoy optimizando la publicación porque ya tengo código de trabajo, pero necesito que vaya más rápido. Los métodos getlist se invocarán con mucha frecuencia y deseo evitar cualquier verificación explícita de tipos.

+0

¿Cuáles son los parámetros en los métodos 'GetList' si no se usan realmente? Parece que podrías exponer las listas como propiedades en lugar de como métodos. – Lee

+0

Así es como solía hacerlo, pero como dije, tengo que tener 3 versiones de cada método que acceden a estas listas. No quiero 3, prefiero una versión genérica. Esto no se puede hacer llamando explícitamente a la lista como una propiedad. Las listas deben ser recuperadas de forma genérica. La lista correcta para elegir tendrá que determinarse usando algún tipo u otro. – Denzil

Respuesta

1

¿Qué tal una mejor implementación para esa mejor interfaz?

public class EntityBucket 
{ 
    public LinkedList<IEntity> Entities; 

    public IEnumerable<T> GetEntities<T>() where T : IEntity 
    { 
    return Entities.OfType<T>(); 
    } 

} 


List<IUndead> myBrainFinders = bucket.GetEntities<IUndead>().ToList(); 

Con esta implementación, la persona que llama mejor agregar un punto a la lista (s) derecha. Ese fue un requisito para su implementación original, así que supongo que no es un problema.

public class EntityBucket 
{ 
    Dictionary<Type, List<IEntity>> entities = new Dictionary<Type, List<IEntity>>(); 

    public void Add<T>(T item) where T : IEntity 
    { 
    Type tType = typeof(T); 
    if (!entities.ContainsKey(tType)) 
    { 
     entities.Add(tType, new List<IEntity>()); 
    } 
    entities[tType].Add(item); 
    } 

    public List<T> GetList<T>() where T : IEntity 
    { 
    Type tType = typeof(T); 
    if (!entities.ContainsKey(tType)) 
    { 
     return new List<T>(); 
    } 
    return entities[tType].Cast<T>().ToList(); 
    } 

    public List<IEntity> GetAll() 
    { 
    return entities.SelectMany(kvp => kvp.Value) 
     .Distinct() //to remove items added multiple times, or to multiple lists 
     .ToList(); 
    } 

} 
+0

Gracias, implementaré esto tan pronto como tenga tiempo. Si es lo suficientemente rápido, regresaré aquí – Denzil

+0

Pero después de ver esto se parece más o menos a lo que tenía en mente. – Denzil

3

Así que quieres una mejor interfaz, porque, como dijiste, pasar un objeto innecesario a GetList solo para descubrir su tipo tiene poco sentido.

Se podría hacer algo como:

public List<IEntity> GetList<T>() : where T:IEntity 
{ 
    if(typeof(T)==typeof(IUndead)) return undedEntities; 
    // and so on 
} 

Y vas a tener que llamar así: GetList<IUndead>();

Creo que una enumeración es una mejor idea aquí:

enum EntityTypes { Undead, Alive, Object }; 
public List<IEntity> GetList(EntityTypes entityType) { ... } 

Es más limpio y tiene más sentido para mí.

EDITAR: Usar genéricos en realidad no es tan simple. Alguien podría llamar a GetList un tipo Zombie, que implementa IUndead, y luego deberá verificar las implementaciones de la interfaz. Alguien podría incluso pasarle un LiveZombie que implementa tanto IUndead como IAlive. Definitivamente ve con una enumeración.

+1

Estoy de acuerdo con hacer una enumeración para esta situación. – Tejs

+0

Me gusta tu respuesta. Sin embargo, lo implementaré antes de aceptarlo. Supongo que no he mencionado esto en mi publicación, pero el rendimiento realmente es un problema aquí. Tengo un poco de miedo de hacer typeof, ya que estas funciones de GetList se llamarán miles de veces por segundo, y de acuerdo con http://msdn.microsoft.com/en-us/library/ms973858.aspx, "Type comparison operations- en este caso, typeof en C#, GetType, is, IsInstanceOfType, etc. son las API de reflexión más baratas, aunque de ninguna manera son baratas ". Tenga en cuenta el "de ninguna manera barato" – Denzil

+0

Definitivamente voy a buscar enums ahora que lo ha mencionado. Lo he aprendido, pero nunca lo he usado prácticamente en ninguna parte. – Denzil

1

¿Qué tal algo como lo siguiente?

public LinkedList<IEntity> GetList(Type type) { 
    if (typeof(IUndead).IsAssignableFrom(type)) return undeadEntities; 
    if (typeof(ILiving).IsAssignableFrom(type)) return livingEntities; 
    if (typeof(IObject).IsAssignableFrom(type)) return objects; 
} 

allí tendría que llamarlo así:

var myUndeads = GetList(typeof(IUndead)); 
var myLivings = GetList(typeof(ILiving)); 
// etc 

El mismo tipo de lógica podría ser implementado en sus eliminaciones, añadir, y otros métodos, y no será necesario una instancia concreta de un objeto para acceder a ellos

La lógica IsAssignableFrom maneja las subclases simplemente bien (es decirpodría tener un CatZombie, que deriva de Zombie, que implementa IUndead, y esto todavía funcionaría). Esto significa que todavía sólo es necesario crear un método de eliminación, algo así como lo siguiente:

public void Delete(IEntity e, World world) { 
    if (typeof(IUndead).IsAssignableFrom(type)) undeadEntities.Remove(e); 
    if (typeof(ILiving).IsAssignableFrom(type)) livingEntities.Remove(e); 
    if (typeof(IObject).IsAssignableFrom(type)) objects.Remove(e); 
} 

EDIT: Noté sus comentarios sobre la respuesta de zmbq respecto al rendimiento; esto definitivamente no es rápido. Si necesita un alto rendimiento, use un enfoque tipo enum. Su código será más detallado y requerirá más mantenimiento, pero obtendrá un mejor rendimiento.

+0

Sí, lo siento. Sin embargo, te daré uno por la idea. ¡Mejor actualizo mi publicación para reflejar esto! Lo siento una vez más – Denzil

+0

Esto es realmente peor que el uso de medicamentos genéricos. Si usa genéricos, el compilador lo atrapará si pasa un String o un DataSet. Y todavía enfrenta el problema de obtener un objeto que implemente IAlive AND IUndead. – zmbq

+1

@zmbq "Y todavía estás enfrentando el problema de obtener un objeto que implemente IAlive AND IUndead". No estoy seguro de lo que quieres decir; 'Delete' manejará las múltiples interfaces sin problemas. ¿Estás hablando de 'GetList' que no maneja múltiples interfaces? En ese caso, su solución tampoco resuelve el problema.La solución para eso no es difícil, solo necesitas combinar los resultados de todas las interfaces relevantes (tu solución también debería hacer eso) – ean5533

0

Me parece que podría implementar un Dictionary de LinkedList con nombre y referirse a ellos por nombre o enum.

De esta forma, agregar o quitar listas es solo un problema de implementación de y no hay clases separadas para tratar.

+0

Así es como lo tenía inicialmente, pero mi código se expandió para tener estos 3 tipos diferentes, que necesito poder manejar por separado. Es por eso que agregué el cubo con 3 listas enlazadas. También intenté hacer 3 diccionarios, pero esa fue la implementación más lenta, ya que los accesos al diccionario son relativamente lentos. – Denzil

Cuestiones relacionadas