Un modo per trasmettere un tipo di base a un tipo derivato

Non sono sicuro se questa sia una cosa strana da fare o no, o se è un po ‘come l’odore del codice … ma mi stavo chiedendo se ci fosse un modo (una sorta di modello di oop sarebbe bello) per “lanciare” un tipo di base a una forma del suo tipo derivato. So che questo ha poco senso in quanto il tipo derivato avrà funzionalità aggiuntive che il genitore non offre, che non è fondamentalmente sano. Ma c’è un modo per farlo? Ecco un esempio di codice in modo da poter spiegare meglio ciò che sto chiedendo.

public class SomeBaseClass { public string GetBaseClassName {get;set;} public bool BooleanEvaluator {get;set;} } public class SomeDerivedClass : SomeBaseClass { public void Insert(SqlConnection connection) { //...random connection stuff cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar; //... } } public static void Main(object[] args) { SomeBaseClass baseClass = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)baseClass; derClass.Insert(new sqlConnection()); } 

So che questo sembra sciocco, ma c’è un modo per realizzare qualcosa di simile?

Non in modo sano, nelle lingue “gestite”. Questo è downcasting , e non esiste un modo per farlo funzionare in modo sano, proprio per la ragione che hai descritto (le sottoclassi forniscono più delle classi base) da dove viene questo “di più”?). Se si desidera realmente un comportamento simile per una particolare gerarchia, è ansible utilizzare i costruttori per i tipi derivati ​​che assumeranno il tipo di base come prototipo.

Si potrebbe build qualcosa con una riflessione che gestisse i casi semplici (tipi più specifici che non hanno stato di aggiunta). In generale, basta riprogettare per evitare il problema.

Modifica: Woops, non può scrivere operatori di conversione tra tipi di base / derivati. Una stranezza di Microsoft che cerca di “proteggerti” contro te stesso. Ah, beh, almeno non sono nemmeno lontani come il sole.

Prova la composizione invece dell’eredità!

A me sembra che sarebbe meglio passare un’istanza di SomeBaseClass su SomeDerivedClass (che non deriverà più dalla class base e dovrebbe essere rinominata come tale)

 public class BooleanHolder{ public bool BooleanEvaluator {get;set;} } public class DatabaseInserter{ BooleanHolder holder; public DatabaseInserter(BooleanHolder holder){ this.holder = holder; } public void Insert(SqlConnection connection) { ...random connection stuff cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar; ... } } public static void Main(object[] args) { BooleanHolder h = new BooleanHolder(); DatabaseInserter derClass = new DatabaseInserter(h); derClass.Insert(new sqlConnection); } 

Controlla http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (pagina 3):

Il riutilizzo del codice tramite composizione Composition fornisce a Apple un modo alternativo per riutilizzare l’implementazione di peel () da parte di Fruit. Invece di estendere Fruit, Apple può mantenere un riferimento a un’istanza Fruit e definire il proprio metodo peel () che richiama semplicemente peel () sul Fruit.

Personalmente non penso che valga la pena di usare l’ereditarietà in questo caso. Invece, basta passare l’istanza della class base nel costruttore e accedervi tramite una variabile membro.

 private class ExtendedClass //: BaseClass - like to inherit but can't { public readonly BaseClass bc = null; public ExtendedClass(BaseClass b) { this.bc = b; } public int ExtendedProperty { get { } } } 

Il downcasting ha senso, se si dispone di un object di class derivata ma viene fatto riferimento a un riferimento di tipo class di base e per qualche motivo si desidera che venga fatto riferimento a un riferimento di tipo class derivato. In altre parole, puoi downcast per invertire l’effetto del precedente upcasting. Ma non puoi avere un object di class base referenziato da un riferimento di un tipo di class derivata.

Non sto dicendo che lo raccomando. Ma potresti trasformare la class base in una stringa JSON e poi convertirla nella class derivata.

 SomeDerivedClass layer = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(BaseClassObject)); 

No, non è ansible. In un linguaggio gestito come C #, semplicemente non funzionerà. Il runtime non lo consentirà, anche se il compilatore lo lascia passare.

Hai detto a te stesso che questo sembra sciocco:

 SomeBaseClass class = new SomeBaseClass(); SomeDerivedClass derClass = (SomeDerivedClass)class; 

Quindi chiediti, la class è in realtà un’istanza di SomeDerivedClass ? No, quindi la conversione non ha senso. Se è necessario convertire SomeBaseClass in SomeDerivedClass , è necessario fornire un tipo di conversione, un costruttore o un metodo di conversione.

Sembra come se la tua gerarchia di classi avesse bisogno di un po ‘di lavoro, però. In generale, non dovrebbe essere ansible convertire un’istanza di class base in un’istanza di class derivata. In genere dovrebbero esserci dati e / o funzionalità che non si applicano alla class base. Se la funzionalità della class derivata si applica a tutte le istanze della class base, allora deve essere inserita nella class base o inserita in una nuova class che non fa parte della gerarchia della class base.

Il linguaggio C # non consente tali operatori, ma puoi comunque scriverli e funzionano:

 [System.Runtime.CompilerServices.SpecialName] public static Derived op_Implicit(Base a) { ... } [System.Runtime.CompilerServices.SpecialName] public static Derived op_Explicit(Base a) { ... } 

Sì, questo è un odore di codice, e praticamente il fatto che la catena ereditaria è rotta.

La mia ipotesi (dal campione limitato) è che preferiresti far funzionare DerivedClass su un’istanza di SomeBaseClass, in modo che “DerivedClass abbia un SomeBaseClass”, piuttosto che “DerivedClass is a SomeBaseClass”. Questo è noto come “composizione di favore sull’eredità”.

Come altri hanno notato, il casting che suggerisci non è realmente ansible. Potrebbe essere un caso in cui il pattern Decorator (Head First extract) può essere introdotto?

Hai mai pensato a un’interfaccia che cosa implementerebbe attualmente la tua class base e la tua class derivata? Non conosco le specifiche del perché stai implementando in questo modo ma potrebbe funzionare.

Questo è chiamato downcasting e il suggerimento di Seldaek di usare la versione “sicura” è valido.

Ecco una descrizione abbastanza decente con esempi di codice .

Questo non è ansible perché come otterrai l’extra che la class derivata ha. Come fa il compilatore a sapere che vuoi dire derivatoClass1 e non derivatoClass2 quando lo installi?

Penso che quello che stai cercando sia il modello factory o simile in modo da poter istanziare oggetti senza conoscere veramente il tipo esplicito che viene istanziato. Nell’esempio, se il metodo “Inserisci” fosse un’interfaccia, tale istanza la fabbrica restituisce le implementazioni.

Non so perché nessuno ha detto questo e potrei aver perso qualcosa, ma puoi usare la parola chiave as e se hai bisogno di fare una dichiarazione if usa if.

 SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass if(class is SomeDerivedClass) ; 

-edit- Ho fatto questa domanda molto tempo fa

Recentemente ho avuto la necessità di estendere un semplice DTO con un tipo derivato per aggiungere ulteriori proprietà. Poi ho voluto riutilizzare una qualche logica di conversione che avevo, dai tipi di database interni ai DTO.

Il modo in cui l’ho risolto era imporre un costruttore vuoto nelle classi DTO, usandolo in questo modo:

 class InternalDbType { public string Name { get; set; } public DateTime Date { get; set; } // Many more properties here... } class SimpleDTO { public string Name { get; set; } // Many more properties here... } class ComplexDTO : SimpleDTO { public string Date { get; set; } } static class InternalDbTypeExtensions { public static TDto ToDto(this InternalDbType obj) where TDto : SimpleDTO, new() { var dto = new TDto { Name = obj.Name } } } 

Posso quindi riutilizzare la logica di conversione dal semplice DTO quando si converte in quello complesso. Naturalmente, dovrò inserire le proprietà del tipo complesso in qualche altro modo, ma con molte, molte proprietà del semplice DTO, questo semplifica davvero le cose IMO.

Questo non può funzionare. Vai a guardare la pagina di aiuto collegata dall’errore di compilazione.

La soluzione migliore è usare qui i metodi di fabbrica.

Come hanno sottolineato molte risposte, non è ansible rinunciare a ciò che ha senso.

Tuttavia, nel tuo caso, SomeDerivedClass non ha proprietà che saranno “mancanti”. Quindi potresti creare un metodo di estensione come questo:

 public static T ToDerived(this SomeBaseClass baseClass) where T:SomeBaseClass, new() { return new T() { BooleanEvaluator = baseClass.BooleanEvaluator, GetBaseClassName = baseClass.GetBaseClassName }; } 

Quindi non stai trasmettendo, semplicemente convertendo:

 SomeBaseClass b = new SomeBaseClass(); SomeDerivedClass c = b.ToDerived(); 

Questo funziona davvero solo se tutti i dati nella class base sono sotto forma di proprietà leggibili e scrivibili.

C ++ lo gestisce usando un costruttore. Typecasting C ++ . Mi sembra una svista. Molti di voi hanno sollevato la questione di cosa avrebbe fatto il processo con le proprietà extra. Vorrei rispondere, cosa fa il compilatore quando crea la class derivata quando il programmatore non imposta le proprietà? Ho gestito questa situazione in modo simile al C ++. Creo un costruttore che prende la class base e quindi imposta manualmente le proprietà nel costruttore. Ciò è decisamente preferibile all’impostazione di una variabile nella class derivata e alla rottura dell’ereditarietà. Lo sceglierei anche su un metodo di fabbrica perché penso che il codice risultante sarebbe più pulito.