0

I have a somewhat complex situation involving inheritance. I have a base DataObject class that my Invoice class inherits from. I want to save the Invoice object using the DataObject save method and then return the Invoice object that was just saved.

public abstract class DataObject {
    public virtual DataObject Save() {
        // Save data
        return this;
    }
}

derived classes

public class InvoiceObject : DataObject {
    // base data for the class goes here
}

public class Invoice : InvoiceObject {
    public override DataObject Save() {
          // Somehow return an Invoice object here
          return base.Save();
    }
}

I want to avoid using casts if possible. Any insight would be helpful. For arguments sake the structure must remain the same. I cannot remove the InvoiceObject object and the Save method must return the entire Invoice object that was saved to the database. I am not even sure if this is possible, but I really hope it is.

coryrwest
  • 700
  • 1
  • 8
  • 23
  • I am not sure if this is what you want, you will have to implement Save() in InvoiceObject and call base.Save() from there as well. and do not return base.Save() from Invoice, instead return this from Invoice? – Siva Aug 23 '13 at 21:47
  • 1
    Can you use an interface instead of class inheritance? – Dan Aug 23 '13 at 21:48
  • I would prefer to use inheritance as that will allow me to implement Save once and change it as needed further down the inheritance tree. – coryrwest Aug 23 '13 at 21:53
  • @Peter almost but not quite. I do not want to specify `Invoice` as a `DataObject`. I only want to specify that `Invoice` is an `InvoiceObject` and from extension is a `DataObject` – coryrwest Aug 23 '13 at 22:03

1 Answers1

3

Working within your constraints, it seems the easiest way to do this is to declare Save as abstract (as well as InvoiceObject), and letting the concrete templatized subclasses override it, like so:

public abstract class DataObject<T> {
     public abstract T Save();
}

public abstract class InvoiceObject<T> : DataObject<T> {
    // base data for the class goes here    
}

public class Invoice : InvoiceObject<Invoice> {
    public override Invoice Save() {            
        return this;
    }
}

EDIT: In response to your comment, having a default "Save" implementation means that whatever object Save returns must be a DataObject<T>, since the default implementation of Save is return this; inside of a DataObject<T> (hope that makes sense).

You can do this by using the Curiously Recurring Template Pattern, but doing so requires that you return a DataObject<T> from your save, like so:

public abstract class DataObject<T> where T : DataObject<T>{
     public virtual DataObject<T> Save(){
        return this;
     }
}

public abstract class InvoiceObject<T> : DataObject<T> where T : DataObject<T> {
    // base data for the class goes here    
}

public class Invoice : InvoiceObject<Invoice> {

}
Community
  • 1
  • 1
DavidN
  • 5,117
  • 2
  • 20
  • 15
  • 1
    +1, but you could give the DataObject a constraint with where T : DataObject and you could remove the InvoiceObject – Jeroen van Langen Aug 23 '13 at 22:07
  • That is quite easy, but I am trying to avoid having to implement `Save` for every object as most of them will use the "default" Save method and very few will have to be implemented differently then the base. – coryrwest Aug 23 '13 at 22:07
  • You could create a Attribute and use reflection in the base class to enumerate all properties that you want te serialize. – Jeroen van Langen Aug 23 '13 at 22:09
  • Not exactly what is was looking for but @DavidN came as close as I think I can get. I decided to get rid of the `InvoiceObject` and make the `DataObject` generic. – coryrwest Aug 23 '13 at 22:15
  • Submitted that last comment before I read the EDIT. Great solution! – coryrwest Aug 23 '13 at 22:16