2012-03-27 9 views
8

Tengo una aplicación que tiene un concepto de Venue, un lugar donde ocurren los eventos. A Venue tiene muchos VenuePart s. Por lo tanto, se ve así:¿Cómo organizo clases de C# que heredan unas de otras, pero también tienen propiedades que heredan unas de otras?

public abstract class Venue 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<VenuePart> VenueParts { get; set; } 
} 

Un Venue puede ser un GolfCourseVenue, que es un Venue que tiene una pendiente y un tipo específico de VenuePart llama HoleVenuePart:

public class GolfCourseVenue : Venue 
{ 
    public string Slope { get; set; } 
    public virtual ICollection<HoleVenuePart> Holes { get; set; } 
} 

En el futuro, también puede haber otros tipos de Venue s que hereden de Venue. Pueden agregar sus propios campos y siempre tendrán VenuePart de su propio tipo específico.

Éstos son los VenuePart clases:

public abstract class VenuePart 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public abstract string NameDescriptor { get; } 
} 

public class HoleVenuePart : VenuePart 
{ 
    public override string NameDescriptor { get { return "Hole"; } } 
    public int Yardage { get; set; } 
} 

Mis declaraciones anteriores parecen mal, porque ahora tengo una GolfCourseVenue con dos colecciones, cuando en realidad sólo debe tener el uno. No puedo anularlo, porque el tipo es diferente, ¿verdad? Cuando ejecuto informes, me gustaría referirme genéricamente a las clases, donde escupo Venue sy VenuePart s. Pero, cuando presento formularios y tal, me gustaría ser específico.

Tengo muchas relaciones como esta y me pregunto qué estoy haciendo mal. Por ejemplo, tengo un Order que tiene OrderItem s, pero también tipos específicos de Order s que tienen tipos específicos de OrderItem s.

Actualización: Debo notar que estas clases son entidades Entity Framework Code-First. Esperaba que esto no importara, pero creo que sí. Necesito estructurar las clases de forma que Code-First pueda crear tablas de forma adecuada. No parece que Code-First pueda manejar genéricos. Lo sentimos, este detalle de implementación se está en el camino de una solución elegante:/

Actualización 2: Alguien vinculado a una búsqueda que apuntó a Covariance and Contravariance, lo que parecía ser una manera de limitar las listas dentro de los subtipos que ser de una determinada subtipo ellos mismos. Eso parece muy prometedor, ¡pero la persona borró su respuesta! ¿Alguien tiene información sobre cómo puedo aprovechar estos conceptos?

Actualización 3: Se han eliminado las propiedades de navegación que se encontraban en los objetos secundarios, porque confundía a las personas y no ayudaba a describir el problema.

Respuesta

2

Aquí es una de las opciones posibles utilizando los genéricos:

public abstract class VenuePart 
{ 
    public abstract string NameDescriptor { get; } 
} 

public class HoleVenuePart : VenuePart 
{ 
    public string NameDescriptor { get{return "I'm a hole venue"; } } 
} 

public class Venue<T> where T : VenuePart 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual Company Company { get; set; } 
    public virtual ICollection<T> VenueParts { get; set; } 
} 

public class GolfCourseVenue : Venue<HoleVenuePart> 
{ 
} 

Aquí GolfCourseVenue tiene las VenueParts colección, que pueden contener HoleVenueParts o clases HoleVenueParts súper. Otras especializaciones de Venue restringirían a VenueParts a contener VenueParts específicos para ese lugar.

Una segunda posibilidad es más o menos como lo ha tenido

public abstract class VenuePart 
{ 
    public abstract string NameDescriptor { get; } 
} 

public class HoleVenuePart : VenuePart 
{ 
    public string NameDescriptor { get{return "I'm a hole venue"; } } 
} 

public class Venue 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual Company Company { get; set; } 
    public virtual ICollection<VenuePart> VenueParts { get; set; } 
} 

public class GolfCourseVenue : Venue 
{ 
} 

Ahora GolfCourseVenue tiene las VenueParts colección, que pueden contener VenueParts o clases VenueParts súper. Aquí todas las especializaciones de Venue pueden contener cualquier tipo de VenuePart que puede o no ser apropiado.

En respuesta a su comentario sobre la covarianza, propondría algo como esto:

public abstract class VenuePart 
{ 
    public abstract string NameDescriptor { get; } 
} 

public class HoleVenuePart : VenuePart 
{ 
    public override string NameDescriptor { get{return "I'm a hole venue"; } } 
} 

public abstract class Venue 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public abstract ICollection<VenuePart> VenueParts { get; } 
} 

public class GolfCourseVenue : Venue 
{ 
    private ICollection<HoleVenuePart> _holeVenueParts; 

    public GolfCourseVenue(ICollection<HoleVenuePart> parts) 
    { 
     _holeVenueParts = parts; 
    } 

    public override ICollection<VenuePart> VenueParts 
    { 
     get 
     { 
      // Here we need to prevent clients adding 
      // new VenuePart to the VenueParts collection. 
      // They have to use Add(HoleVenuePart part). 
      // Unfortunately only interfaces are covariant not types. 
      return new ReadOnlyCollection<VenuePart>(
        _holeVenueParts.OfType<VenuePart>().ToList()); 
     } 
    } 

    public void Add(HoleVenuePart part) { _holeVenueParts.Add(part); } 
} 
+0

Agregué las clases Part para que pudieras ver por qué tenía que haber 'abstract'. Pero, tienes razón sobre Venue. Eso no necesita ser abstracto. – Chris

+0

¿Puedo usar [covarianza] (http://msdn.microsoft.com/en-us/library/dd799517.aspx) en el constructor de GolfCourseVenue para restringir su colección VenuePart a HoleVenueParts? ¿Estás familiarizado con los altibajos de esto? No creo que pueda utilizar su solución genérica porque EF no maneja esto. – Chris

1

Puede utilizar Covariance

public abstract class Venue 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual Company Company { get; set; } 
    public virtual IEnumerable<VenuePart> VenueParts { get; set; } 
} 

public class GolfCourseVenue : Venue 
{ 
    public string Slope { get; set; } 

    public GolfCourseVenue() 
    { 
     List<HoleVenuePart> HoleVenueParts = new List<HoleVenuePart>(); 
     HoleVenueParts.Add(new HoleVenuePart()); 
     VenueParts = HoleVenueParts; 
    } 
} 

Suponiendo HoleVenuePart se hereda de VenuePart

+0

¡Su suposición es correcta y esto parece prometedor! Leeré sobre estos temas. – Chris

1

miro hacia adelante al consejo de otros, pero mi enfoque es usar medicamentos genéricos en este caso. ¡Con genéricos, las "partes" de su GolfCourseVenue tienen un fuerte tipado!

... y mientras escribo esto, todos los demás dicen genéricos también. ¿CÓMO HACE que los overstackers digan tan rápido?

De todas formas, pretendiendo que todavía estoy primero -

public class VenuePart 
{ 
} 

public class HoleVenuePart : VenuePart 
{ 
} 

public abstract class Venue<T> where T : VenuePart 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual Company Company { get; set; } 
    public virtual ICollection<T> Parts { get; set; } 
} 

public class GolfCourseVenue : Venue<HoleVenuePart> 
{ 
    public string Slope { get; set; } 
} 

Además, como segunda opción, se puede usar una interfaz demasiado, por lo que en caso de que no le gustó el nombre Parts, que se podría llamar Holes cuando el tipo derivado es conocido por ser un campo de golf

public class VenuePart 
{ 
} 

public class HoleVenuePart : VenuePart 
{ 
} 

public interface IPartCollection<T> where T : VenuePart 
{ 
    ICollection<T> Parts { get; set; } 
} 

public abstract class Venue<T> : IPartCollection<T> where T : VenuePart 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual Company Company { get; set; } 
    public virtual ICollection<T> Parts { get; set; } 
} 

public class GolfCourseVenue : Venue<HoleVenuePart> 
{ 
    public string Slope { get; set; } 
    ICollection<HoleVenuePart> IPartCollection<HoleVenuePart>.Parts { get { return base.Parts; } set { base.Parts = value; }} 

    public virtual ICollection<HoleVenuePart> Holes { get { return base.Parts; } set { base.Parts = value;}} 
} 
+0

Casi nunca respondo nada antes de que entren otras personas =)/Agradezco su respuesta aunque sea similar, porque me da contexto adicional. ¡Vota por ti! – Chris

0

Si quita "set" porciones de ambas colecciones de lo que tendrá más sentido: clase base proporciona "todas las partes" colección, vista mientras que las clases derivadas han filtrado además de cl base culo uno

Nota: Dependiendo de sus necesidades haciendo GolfVenue para ser una especialización genérica de Venue<VenuePart> puede no funcionar como Venue<Type1> y Venue<Type2> no tendrá ninguna buena clase base para trabajar.

Considere el uso de interfaces en lugar de clases base, ya que permitiría una mayor flexibilidad en la implementación.

public interface IVenue 
{ 
    public int Id { get; } 
    public string Name { get; } 
    public virtual IEnumerabe<VenuePart> VenueParts { get; } 
} 

public interface IGolfCourse : IVenue 
{ 
    public virtual IEnumerabe<HoleVenuePart> Holes { get; } 
} 

Ahora puede utilizar GolfCourse: Lugar de otras muestras, pero ya que implementa la interfaz se puede manejar en forma gnereic también:

class GolfCourse:Venue<HoleVenuePart>, IGolfCourse { 
    public virtual IEnumerabe<VenuePart> Holes{ get 
    { 
     return VenueParts.OfType<HoleVenuePart>(); 
    } 
    } 
} 
class OtherPlace:Venue<VenuePart>, IVenue {...} 

List<IVenue> = new List<IVenue> { new GolfCourse(), new OtherPlace() }; 

Nothe que GolfCourse y OtherPlace no tienen clase padre común (excepto objeto), entonces sin interfaz no puede usarlos de manera intercambiable.

+0

Tengo algunos informes que escuchan tablas de lugares que están en uso, cuántas partes tienen, qué partes están en uso, etc. Por lo tanto, tengo que referirme a ellas genéricamente. ¿Podría ampliar cómo podría utilizar las interfaces de una manera que es mejor que los tipos derivados? – Chris

+0

actualizado con bits para interfaces. –

Cuestiones relacionadas