2012-03-15 24 views
5

que tienen las siguientes declaraciones de interfaz:¿Cómo hago referencia a un elemento primario de un objeto secundario con interfaces genéricas en C#?

interface IOrder<T> where T: IOrderItem 
{ 
    IList<T> Items { get; set; } 
} 

interface IOrderItem 
{ 
    IOrder<IOrderItem> Parent { get; set; } // What do I put here? 
} 

Quiero que los elementos de la lista para tener una referencia al objeto de encabezado, por lo que puede utilizar el ID y otros campos de la cabecera.

En mis clases concretas, se queja de que no implemento "Parent" correctamente.

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public StoreOrder Parent { get; set; } // This doesn't satisfy the interface 
} 

I intentado fijar IOrderItem como IOrderItem<T> y pasando el tipo de padres, pero que conducen a la referencia circular ya que la clase Cabecera requries el tipo de clase de artículo ... me confundí.

¿Algún consejo sobre cómo implementar esto correctamente?

+0

[Esto puede ser interesante para usted] (http://msdn.microsoft.com/en-us/library/dd469487.aspx) – abatishchev

Respuesta

3

Si define sus interfaces de esta manera:

interface IOrder<T> where T : IOrderItem<T> 
{ 
    IList<T> Items { get; set; } 
} 
interface IOrderItem<T> where T : IOrderItem<T> 
{ 
    IOrder<T> Parent { get; set; } 
} 

A continuación, puede aplicar como esto para obtener la funcionalidad que se espera:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 
class StoreOrderItem: IOrderItem<StoreOrderItem> 
{ 
    public IOrder<StoreOrderItem> Parent { get; set; } 
} 
+0

esto corrige el error del compilador, pero las tiras del tipo de la propiedad '' Parent' de nuevo a iOrder 'en lugar de' StoreOrder' como parecía que el interrogador estaba tratando. Por lo tanto, si usted tiene un 'StoreOrderItem' y quiere hacer nada con el' Parent' además de operar en la propiedad 'iOrder .Items' tendrá que emitir el' 'Parent' a StoreOrder' primero. – devgeezer

+2

@devgeezer - Estás en lo cierto. Sin embargo, mi implementación permitirá que cualquier implementación 'IOrder ' sea la principal. Cualquier cosa que el '' IOrderItem necesita saber acerca de su padre debe ser implementado en el iOrder '' interfaz, ya que la relación entre los dos se define a nivel de interfaz y no el nivel de aplicación. –

+0

Excelente punto. Estoy pensando que leí demasiado intento en la pregunta. +1 – devgeezer

1
class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public IOrder<IOrderItem> Parent { get; set; } // This doesn't satisfy the interface 
} 

No puede especializarse - IOrder<IOrderItem> es más general que StoreOrder

+0

Por qué ID? ¿Por qué no 'IOrder .ilist '? – abatishchev

+0

Tienes razón, no hay idea de por qué. Editado – IronicMuffin

+0

@Eugen: ¿Es su código compilable? – abatishchev

0

Declaración para satisfacer las interfaces:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    // interface members 
    public IList<StoreOrderItem> Items { get; set; } 

    // own members 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{ 
    public IOrder<IOrderItem> Parent { get; set; } 
} 

Para acceder a los miembros personalizados que tendrá que emitir:

class StoreOrderItem : IOrderItem 
{ 
    void Test() 
    { 
     int id = ((StoreOrder)this.Parent).ID; 
    } 
} 
1

Aquí es una solución para el cambio de las interfaces:

interface IOrder<TOrder, TOrderItem> 
    where TOrderItem : IOrderItem<TOrder> 
{ 
    IList<TOrderItem> Items { get; set; } 
} 

interface IOrderItem<TOrder> 
{ 
    TOrder Parent { get; set; } 
} 

Cambios a StoreOrder y StoreOrderItem para admitir los cambios de interfaz Y agregando un par de propiedades a cada uno para una prueba posterior:

class StoreOrder: IOrder<StoreOrder, StoreOrderItem> 
{ 
    public DateTime Date { get; set; } 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem<StoreOrder> 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } 
} 

... y ahora la creación de StoreOrder y StoreOrderItem casos, y ponerlos a prueba:

void Main() 
{ 
    var so = new StoreOrder { Date = DateTime.Now }; 
    var item = new StoreOrderItem { 
      Parent = so, 
      ItemName = "Hand soap", 
      ItemPrice = 2.50m }; 
    so.Items = new [] { item }; 

    Console.WriteLine(item.Parent.Date); 
    Console.WriteLine(so.Items.First().ItemName); 
} 

... cuando es ejecutado, impresos

3/16/2012 10:43:55 AM 
Hand soap 

Otra opción es desechar lo anterior y tomar this solution y modificarlo agregando la propiedad Parent con el tipo deseado y utilizando la implementación de interfaz explícita para evitar el lanzamiento en los sitios de llamadas, haciendo para una aplicación StoreOrderItem algo como esto:

class StoreOrderItem : IOrderItem 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } // note: original implementation 

    IOrder<IOrderItem> IOrderItem.Parent { // explicit interface implementation 
     get { return (IOrder<IOrderItem>)this.Parent; } 
     set { this.Parent = (StoreOrder)value; } 
    } 
} 

Mi favorito de lo anterior es la primera propuesta de referencia con los parámetros de dos genéricas a IOrder y el Libre genérica parámetros de IOrderItem. Una versión anterior que había publicado y ahora he editado tenía ambas interfaces, cada una con los mismos dos tipos genéricos, cada una con las mismas restricciones.Sentí que esto estaba yendo un poco por la borda, así que lo reduje a la implementación anterior. Aunque hay una completa falta de limitaciones de parámetro de tipo TOrder a IOrderItem - intentos de eludir otros tipos en su lugar (por ejemplo, object) dio lugar a errores de compilación. El uso de TOrder en lugar de simplemente llamarlo T proporciona una pista sobre el tipo esperado en ausencia de la restricción de tipo. Esa será mi edición final. Siento que es el más breve de mis intentos; si tiene curiosidad, puedo proporcionar la implementación anterior que tenía los tipos doblemente genéricos restringidos en las interfaces, pero esta es al menos mi preferida esta solución. ¡aclamaciones!

+0

Desde entonces, aprendí que mi primera solución tiene un nombre: "Patrón de plantilla curiosamente recurrente". Etiqueta SO [ctrp]. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – devgeezer

Cuestiones relacionadas