Search code examples
c#.netoopinterfaceconcrete

IEnumerable versus List in a concrete class implementation


I am writing a class library for financial modeling.

I want to define the models using interfaces to create abstraction for testing, and the possible later addition of new concrete classes.

I am struggling on how to define lists of things, so I can later add to those lists in the concrete class.

For example, if I define an interface as having a list of expenses in my Model called

IEnumerable Expenses {get; set;}

and later want to create a concrete class called ABCModel with a list of concrete XYZExpense in it, what would be the best way to implement the lists so I can easily add more items to the list?

I cannot cast the List as a IEnumerable.

So for example:

 public interface IFinancialModel
{
    IEnumerable<IExpense> Expenses { get; }
    IEnumerable<IRevenue> Revenue { get; }
    IEnumerable<IAsset> Assets { get; }
    IEnumerable<ILiability> Liabilities { get; }
}

I need a concrete class called public class FinancialModel : IFinancialModel

and be able to add concrete items.

Should I create 'Add' methods of my own? Ideally I would like to leverage built in List functionality and not reinvent the wheel.


Solution

  • It depends on what you actually want to do. Having List or IList properties is not recommended, because you can do everything with it (like Clear, Add and so on) without the owning class know about it. This is not good for encapsulation. You wouldn't be able to do something on change (like setting a dirty flag, fire changed events or do validation).

    IEnumerable is a good choice.

    You may add setters, but you shouldn't take the value that is set to the property by reference. It could be anything, like a Linq query or even a database query, which you don't want to reference to. You only want the items. (Also consider that the reference to the collection which comes from the outside can be assigned to other instances, which results in very bad side effects when it is changed there but shouldn't be changed here.)

    // example implementation with a setter
    private List<IExpense> expenses 
    
    public IEnumerable<IExpense> Expenses 
    { 
      get { return expenses; }
      set { expenses = value.ToList(); }
    }
    

    Alternatively you may implement your own Add, AddRange, Remove or Clear etc. methods. However, if the main use case is to set all items in one go, you can only have a SetExpenses method, which is the same as the setter on the property I showed before. You may consider to keep it as an array internally.