2009-07-15 9 views
6

Sono di fronte a un problema che non so come risolvere e spero che la comunità possa aiutare.Downcasting in C#

Sto scrivendo un app che gestisce gli oggetti "guidare". (Questi sono i lead di vendita). Una parte del mio programma importerà i lead da un file di testo. Ora, il file di testo contiene molti potenziali lead, alcuni dei quali voglio importare e alcuni non lo farò.

Per facilità di programmazione (e l'uso), sto analizzando il file di testo in un elenco <piombo> oggetto, e utilizzando un DataGridView per visualizzare i contatti impostando la proprietà DataSource del DataGridView.

Quello che voglio fare è aggiungere una colonna alla griglia, chiamato "Importa", con una casella di controllo che l'utente può controllare per indicare se ciascun cavo deve essere importato.

Il mio primo pensiero è quello di derivare una classe da piombo:

 
public Class LeadWithImportCheckbox : Lead 
{ 
    bool bImport = false;

public bool Import { get { return bImport;} set { bImport = value;} } }

Tuttavia, il motore di analisi restituisce un elenco di oggetti di piombo. Non riesco a ritrasmettere un lead a un LeadWithImportCheckbox. Questo non funziona:

 
LeadWithImportCheckbox newLead = (LeadWithImportCheckbox)LeadFromParsingEngine; 
Questo è un cast non valido.

L'altra opzione che vedo è quello di creare un costruttore per LeadWithImportCheckbox:

 
public LeadWithImportCheckbox(Lead newlead) 
{ 
    base.Property1 = newlead.Property1; 
    base.Property2 = newlead.Property2; 
    .... 
    base.Property_n = newlead.Property_n; 
} 
Questo è problematico per due ragioni. Uno, l'oggetto Lead ha diverse dozzine di proprietà e scrivere questo costruttore è un PITA.

Ma il peggio, se mai cambiare la struttura di base di piombo, ho bisogno di ricordare a tornare indietro e cambiare questo costruttore per LeadWithImportCheckbox. Questo è un pericolo per la manutenzione del mio codice.

Esiste un modo migliore per raggiungere il mio obiettivo?

+0

Solo un pensiero casuale: è possibile che si desideri monitorare se un lead è stato "importato" o meno (immesso manualmente)? In altre parole, potrebbe essere fattibile per "importare" una proprietà permanente di "Piombo"? – Adrien

+0

Non penso che sarebbe qualcosa che vorrei tracciare. Vedo i problemi che questo risolverebbe ma in realtà non fa parte di ciò che voglio veramente realizzare. –

risposta

6

o, al fine di evitare l'aspetto pita, l'uso di riflessione ... (provare questo ...)

EDIT: utilizzare la proprietà, non campo come avevo inizialmente scritto ...

public class NewLead : Lead 
{ 
    public bool Insert; 
    public NewLead(Lead lead, bool insert) 
    { 
     Insert = insert; 
     foreach (PropertyInfo pi in typeof(Lead).GetProperties()) 
      GetType().GetProperty(pi.Name).SetValue 
       (this, pi.GetValue(lead,null), null); 
    } 
} 
+0

Penso che alla fine mi piace l'approccio alla riflessione. (Mi manca solo la comprensione.) Nel codice di esempio, ottengo un array a lunghezza zero da typeof (Lead) .GetFields() –

+0

Credo che questa sia la soluzione su cui mi appresto. Grazie, Charles! Il codice che ho implementato e che sembra funzionare è:

 public LeadWithImportCheckbox(Lead lead):base() { Type LeadType = typeof(Lead); PropertyInfo[] properties = LeadType.GetProperties(); foreach (PropertyInfo fi in properties) { object value = fi.GetValue(lead, null); fi.SetValue(this, value, null); } } 

+0

Soluzione fantastica. Mi piace non dover aggiornare manualmente il mio costruttore se la classe base cambia. – jocull

1

Che cosa si vuole fare è visualizzare la colonna casella sulla griglia e non lo hanno correlato a tutti per i vostri oggetti di piombo. Utilizzi le colonne contrassegnate (e possibile l'Elenco originale) per creare un nuovo insieme di List che sarà la tua lista di importazione.

Poi gestire tutto ciò che si desidera fare con la lista appena creata.

Edit: Una cosa da stare attenti quando si lavora con le liste è il fatto ogni oggetto classe è in realtà solo un puntatore alla classe, quindi se si lavora con l'elenco originale e fare qualcosa di simile:

List<Lead> Importable = new List<Lead>(); 

for(int i=0, i++, i<viewGrid.Count) 
    if(viewGrid[i].CheckedColumn.Checked) 
     Importable.Add(OriginalList[i]); 

Gli oggetti saranno presenti in entrambi gli elenchi e se si modificano i dati di un derivato su entrambi gli elenchi, entrambi verranno modificati.

+0

Inizialmente ho cercato questa soluzione. Funziona in modo appropriato se utilizzo una lista . Tuttavia, se utilizzo un wrapper SortableBindingList , i segni di spunta non rimangono con i lead quando il DataGridView è ordinato. Poiché il file di testo avrà ~ 1.000 potenziali lead, l'ordinamento è importante. (Mi scuso per non aver incluso questo nella dichiarazione del problema originale, è difficile sapere quanta informazione è sufficiente ma non troppo quando si semplifica per un post.) –

4
public class LeadListItem 
{ 
    public Lead Lead { get; set; } 
    public bool ShouldImport { get; set; } 
} 

cioè non copiare il contenuto dell'oggetto piombo, basta memorizzare un riferimento ad esso in un nuovo oggetto LeadListItem, che aggiunge informazioni extra "al di fuori" l'oggetto originale.

Se si desidera che le proprietà del Piombo a comparire in rete, non v'è quasi certamente un modo di fare questo. Perché non chiederlo a questa domanda, invece di sminuire me per dirti la risposta giusta a questa domanda!

+0

Il problema con questa soluzione è che i membri di Lead non mostrano su in DataGridView. –

+0

@The Demigeek Certo che puoi, ma questa è una domanda diversa. – exclsr

+0

GreenReign, come è questa una domanda diversa? La domanda originale riguardava la visualizzazione di una derivazione con una casella di controllo in un oggetto DataGridView. Una soluzione che non include i membri di Lead in DataGridView non è una soluzione al problema. –

3

Un paio di opzioni che potrebbe avere perso:

  • è possibile aggiornare l'oggetto di piombo in sé ad avere una proprietà di importazione (valore predefinito false).
  • È possibile che l'oggetto "ImportLead" consideri il lead come payload (anche se lo rende generico, se lo si desidera), quindi non è necessario il grande costruttore.
  • Creare un nuovo elenco di oggetti lead o enumerabile che contenga solo gli oggetti che si desidera importare in primo luogo.
+0

Vorrei andare con la creazione di una proprietà di importazione sulla classe Lead stessa –

+0

Molto più eloquentemente ha dichiarato il mio maldestro tentativo sopra, ma praticamente quello che stavo cercando di dire. – Adrien

+4

Non sarei d'accordo con Stan. Non vi è alcun motivo per la proprietà di importazione sul lead, non è completamente correlato al lead e l'inquinamento del tuo oggetto dominio. È solo un artefatto dell'interfaccia utente e quindi non dovrebbe modificare gli oggetti del tuo dominio in alcun modo. –

1

Si può solo bassi, se l'oggetto ad essere abbattuto è davvero un oggetto di quel tipo.

Un modo più semplice per risolvere il problema sarebbe quello di avere una classe DisplayLead, come ad esempio:

public class DisplayLead { 
     Lead lead; 
     bool bImport; 
    } 

che sarebbe anche aiutare a separare i dati memorizzati dalla loro rappresentazione in una GUI.

+0

Se lo faccio, le proprietà di lead non vengono visualizzate in DataGridView. –

+1

Con le informazioni fornite in risposta al mio post, questa è la soluzione ottimale. Quello che devi fare è disattivare la generazione automatica delle colonne e creare manualmente le colonne e utilizzare le espressioni DataBinding per raggiungere gli oggetti del contenitore reali in modo da poter leggere all'interno di oggetti complessi. Se cerchi C# Databinding Eval o simili in google dovresti vedere come farlo. –

0

Come soluzione rapida e sporca, è possibile creare l'oggetto 'checkbox' come oggetto diverso che contiene un'istanza di Lead.

public GridLead { 
    public bool Import { get; set; } 
    public Lead Lead { get; set; } 
} 

In questo modo si possono facilmente aggiungere più proprietà 'griglia' a questo oggetto, pur sempre mantenendo un riferimento ai dettagli di piombo senza hardcoding proprietà clonazione in esso.

+0

Se si esegue questa operazione, le proprietà di lead non vengono visualizzate in DataGridView. –

0

Consiglia di provare a modificare (aggiornamento) gli oggetti lead importati.

provare a partire con gli esempi here ...

1

non posso bassi per qualcosa che non è. Se l'oggetto è stato istanziato come Lead, quindi non può essere downcast a qualsiasi classe derivata. Se è stato istanziato come LeadWithImportCheckbox e quindi restituito al codice come Lead, è possibile eseguirne il downcast.

Protezione: controllare il tipo in fase di esecuzione con l'operatore is.

0

Se la classe di piombo ha avuto un costruttore di copia (ad esempio, "Lead (Piombo otherLead)"), LeadWithImportCheckbox avrebbe ereditato questo e si può solo chiamare il costruttore, portano nel costruttore LeadWithImportCheckbox - quindi senza necessità di LeadWithImportCheckbox di essere a conoscenza di i dettagli di piombo.

1

Ci sono molti modi per farlo, ma il modo "giusto" schiocca a causa di quello che hai detto, qui:

Per facilità di programmazione (e l'uso), io sono l'analisi del testo file in un oggetto Elenco e utilizzando un DataGridView per visualizzare i lead tramite impostando la proprietà DataSource del DataGridView .

Quello che voglio fare è aggiungere una colonna alla griglia, denominata "Import", con una casella di controllo che l'utente può controllare per indicare se ogni elettrocatetere dovrebbero essere importati.

vostro Lead sta bene da solo, e si desidera collegare alcuni metadati ad esso - non si vuole creare un altro Lead classificazione (vale a dire la classe LeadWithImportCheckbox).

Quindi, l'approccio migliore nel tuo caso è quello di avere una classe in questo modo:

public class LeadInfo 
{ 
    private Lead lead; 
    private bool shouldImport; 

    public LeadInfo(Lead lead) 
    { 
     this.lead = lead; 
     this.ShouldImport = false; 
    } 

    public bool ShouldImport 
    { 
     get { return shouldImport; } 
     set { shouldImport = value; } 
    } 
} 

Questa scala bene quando si desidera aggiungere altri metadati alla vostra lista, come se si desidera inviare a te stesso con la posta elettronica promemoria su di loro ogni settimana.

+0

Ma se lo faccio, le proprietà dell'elettrocatetere non vengono visualizzate su DataGridView. –

1

Ho visto la soluzione corretta elencata così tante volte che mi sento come se un tallone lo inviasse di nuovo, ma il modo migliore per avvicinarsi a questo è scrivere un wrapper per l'oggetto Lead che include il flag di importazione.

Se le proprietà dell'oggetto Lead non vengono visualizzate in GridView perché si esegue il collegamento di dati con l'oggetto, quindi si scrivono le proprietà passthrough che replicano le proprietà Lead sull'oggetto wrapper.

Il problema è che si desidera visualizzare all'utente qualcosa che non sia una parte intrinseca del modello di dati. La risposta è racchiudere i dati prima di presentarli all'utente in modo da poter controllare ciò che vedono senza modificare il modello sottostante.

Se si è preoccupati che l'oggetto Lead cambierà così tante volte in futuro che le modifiche al wrapper saranno ingombranti, è possibile esaminare la generazione dinamica del codice basata sull'oggetto Lead che genererà automaticamente un oggetto wrapper con gli stessi campi dell'oggetto Lead più il flag di importazione. Anche se francamente, questo è molto più lavoro di quanto tu abbia probabilmente bisogno di qualcosa di così semplice come questo.

+0

Non credo che questa sia la soluzione ottimale. Se la struttura dell'oggetto Lead sottostante cambia, quindi ho bisogno di tornare indietro e modificare il codice che lega le proprietà del Lead a DataGridView. L'ereditarietà risolve questo problema. Indipendentemente dalle modifiche apportate a Lead, se LeadWithImportCheckbox eredita da Lead, non è necessario apportare modifiche al codice. E 'LeadThatIMightWantToImport' Is-A 'Lead'. L'ereditarietà è appropriata. Credo che l'uso di Reflection, come suggerito da Charles, sarà il modo giusto per creare un LeadWithImportCheckbox da un lead. –

+0

L'approccio di riflessione allevierebbe il problema delle "modifiche agli oggetti Lead sottostanti", ma come ho detto, se il tuo oggetto Lead sta cambiando così spesso, ci sono problemi più profondi delle tue pratiche di codifica. Basandoti sulla tua risposta alla soluzione di Charles, non sono sicuro che anche la riflessione sarà la scelta giusta per te. Si noti inoltre che l'uso della riflessione in questo modo imporrà una seria penalizzazione delle prestazioni (potrebbe essere un problema se si inizia ad avere molte migliaia di lead). – genki