2012-03-15 14 views
5

Ho le seguenti dichiarazioni di interfaccia:Come faccio a fare riferimento a un genitore da un oggetto figlio con interfacce generiche in C#?

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

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

voglio le voci della lista per avere un riferimento all'oggetto di testa, quindi è possibile utilizzare l'ID e altri campi dall'intestazione.

Nelle mie classi concrete, si lamenta che non implemento correttamente "Parent".

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

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

ho provato ad installare IOrderItem come IOrderItem<T> e passando nel tipo genitore, ma che portano a riferimento circolare in quanto la classe Header requries il tipo di classe Item ... mi sono confuso.

Qualche consiglio su come implementarlo correttamente?

+0

[Potrebbe essere interessante per voi] (http://msdn.microsoft.com/en-us/library/dd469487.aspx) – abatishchev

risposta

3

Se si definiscono le interfacce in questo modo:

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

È quindi possibile implementare loro come questo per ottenere la funzionalità che ci si aspetta:

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

Questo corregge l'errore del compilatore, ma rimuove il tipo della proprietà 'Parent' su' IOrder 'invece di' StoreOrder' come se il questioner stesse tentando. Quindi, se hai un 'StoreOrderItem' e vuoi fare qualcosa con' Parent' oltre ad operare sulla proprietà 'IOrder .Items' devi prima gettare' Parent' su 'StoreOrder'. – devgeezer

+2

@devgeezer - Hai ragione. Comunque la mia implementazione permetterà a qualsiasi implementazione di 'IOrder ' di essere il genitore. Tutto ciò che l'oggetto 'IOrderItem ' deve conoscere sul suo genitore dovrebbe essere implementato nell'interfaccia 'IOrder ', poiché la relazione tra i due è definita a livello di interfaccia e non a livello di implementazione. –

+0

Punto eccellente. Sto pensando di leggere troppo l'intento nella domanda. +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 
} 

Non possono specializzarsi - IOrder<IOrderItem> è più ampio di StoreOrder

+0

Perché ID? Perché no 'IOrder .IList '? – abatishchev

+0

Hai ragione, nessun indizio sul perché. Modificato. – IronicMuffin

+0

@Eugen: il tuo codice è compilabile? – abatishchev

0

Dichiarazione di soddisfare le interfacce:

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; } 
} 

Per accedere membri personalizzati si dovrà lanciare:

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

Ecco una soluzione per la modifica delle interfacce:

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

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

apportare modifiche al StoreOrder e StoreOrderItem per supportare l'interfaccia cambia E aggiungendo un paio di proprietà a ciascuno per un test successivo:

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; } 
} 

... e ora la creazione di StoreOrder e StoreOrderItem casi, e metterle alla prova:

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); 
} 

... quando viene eseguito, stampata:

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

Un'altra opzione è per rimuovere quanto sopra e prendere this solution e modificarlo aggiungendo la proprietà Parent con il tipo desiderato e utilizzando l'implementazione dell'interfaccia esplicita per evitare il cast dei siti di chiamata, Perfetta per un qualcosa di StoreOrderItem implementazione di simile:

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; } 
    } 
} 

Il mio preferito di quanto sopra è la prima proposta di cui sopra con i parametri di due generici a IOrder e non vincolato generic-parametro IOrderItem. Una versione precedente che avevo pubblicato e che ora avevo modificato aveva entrambe le interfacce con gli stessi due tipi generici ciascuno con gli stessi vincoli.Mi sono sentito come se questo fosse un po 'esagerato, quindi l'ho ridimensionato all'implementazione di cui sopra. Sebbene sia presente una completa mancanza di vincoli sul parametro di tipo TOrder su IOrderItem - i tentativi di fudge altri tipi al suo posto (ad es., object) hanno provocato errori di compilazione. L'utilizzo di TOrder invece di chiamarlo semplicemente T fornisce un suggerimento sul tipo previsto in assenza del vincolo di tipo. Questa sarà la mia modifica finale - ritengo che sia il più succinto dei miei tentativi; se sei curioso, posso fornire la precedente implementazione che aveva i tipi double-generic-constrained sulle interfacce, ma questa è almeno la mia soluzione preferita. Saluti!

+0

Da allora ho appreso che la mia prima soluzione ha un nome: "Modello di modello che ricorre in modo curioso". Tag SO [ctrp]. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – devgeezer

Problemi correlati