2

I've recently read something about using interfaces when exposing collections instead of concrete implementations (IEnumerable instead of List). I'm trying to do that now in my code. However, when I expose a property that return IEnumerable, I'm having some difficulty of not allowing nulls as a return value. Example:

public class HumanResource
{
    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            // return what?
        }
    }
}

What should I return in the getter? I don't want to use automatic properties for this as I want to avoid nulls. What I want is to return a new collection with no items. Of course I can return any type that implements IEnumerable but how will the external user of the class know that? Or did I understand this exposing interface instead of concrete implementations wrong?

EDIT: Removed setter

g_b
  • 11,728
  • 9
  • 43
  • 80
  • 1
    Do you need to have a setter at all? It's really unclear what you're trying to achieve here... where do you want the information to come from? – Jon Skeet Dec 31 '14 at 11:40
  • Hi Jon. Lets say the information is from a database. We put the data in a collection that implements IEnumerable (lets say List). For an external user of the class, he knows that the property returns an IEnumerable but he could assign it to an Array or any other collection that implements IEnumerable but it will cause an error. – g_b Dec 31 '14 at 12:00
  • 1
    No, they *can't* assign it to an array or other type of variable - not without casting. They could create a new array from the sequence, but that's a different matter. The point of exposing an interface is that the caller shouldn't *care* about the implementation type. – Jon Skeet Dec 31 '14 at 12:01
  • I'm not sure I get it but from what you typed, what I understood by that is that it is OK to return a List or any other collection that implements IEnumerable and let the caller worry about creating his own if he needs one? If that's the case, if it were you, what would you return here? – g_b Dec 31 '14 at 12:15
  • I don't know what I'd return - it depends on how you're fetching it from the database, whether you're caching it, whether you trust the callers not to cast back to `List` and mutate the collection etc. – Jon Skeet Dec 31 '14 at 12:24

3 Answers3

5

Of course I can return any type that implements IEnumerable but how will the external user of the class know that?

They don't have to know that, that's exactly the point.

Your property promises to return an IEnumerable<EmplyeeModel>, and that's exactly what happens. It doesn't matter which class implementing this interface your code returns.

What I want is to return a new collection with no items.

So, Enumerable.Empty<EmplyeeModel>() or new List<EmployeeModel>() will do just fine.


When designing an API you need to think about what the consumers will do with the data types you return, and decide upon that accordingly.

Usually an IEnumerable<T> for collections suits everyone. When they want it in a list, they can do new List<T>(yourEnumerable), or yourEnumerable.ToArray() to use it as an array.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Hi, thanks for the comment. From what I gathered in what you type, it is OK to return any concrete class implementing IEnumerable and the caller shouldn't rely on that so he should create his own based on ours if he needs one (when I say OK, I mean it is an accepted practice)? Thanks – g_b Dec 31 '14 at 12:23
  • Yes, that's the point. You can't return an interface, you must return an implementation. Which implementation that is doesn't matter, as you promise to return any class implementing that interface. – CodeCaster Dec 31 '14 at 12:39
  • 1
    OK, one last question. Thinking about it, it seems I don't fully understand the benefits of doing this. I could return a concrete List (instead of IEnumerable) and not only would the user know the type returned, but he can still create his own collection based off of this. When is this method beneficial? Sorry, I thought it was clear to me, but after your answer, I got confused as to when is this beneficial or why is it done this way when it is done this way. Could you give your thoughts on this? – g_b Dec 31 '14 at 12:54
  • That question has been answered before in various forms - see for example [C# - List or IList](http://stackoverflow.com/questions/400135/c-sharp-listt-or-ilistt). :) – CodeCaster Dec 31 '14 at 13:00
1

What I want is to return a new collection with no items.

Properties let you do that very easily:

public class HumanResource
{
    // This is the real employees that gets returned when its not null
    private IEnumerable<EmployeeModel> employees; // may be null

    // This is the empty IEnumerable that gets returned when employees is null
    private static readonly IEnumerable<EmployeeModel> EmptyEmployees =
        new EmployeeModel[0];

    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            return employees ?? EmptyEmployees;
        }
        set {};
    }
}

The code returns an empty array when employees variable is set to null. You can set employees to a collection of any type that implements IEnumerable<EmployeeModel>, or even to an array if you prefer. This is possible because you return by interface.

The flip side of this, of course, is that the clients would have no direct access to methods of properties that are not exposed through the interface. For example, if employees is actually a List, the callers would have to use LINQ's Count() instead of obtaining .Count directly. Of course you can expose a different interface, say, IList<EmployeeModel>, to let your clients use additional methods.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0

You still need to provide an internal backing collection for the property in your class. You can initialize the collection in the constructor, or in the field declaration:

public class HumanResource
{
   private readonly IList<EmployeeModel> _employees = new List<EmployeeModel>();

   public IEnumerable<EmployeeModel> Employees
   {
     get
     {
         return _employees;
     }
     // No Setter - callers may only enumerate the collection
  }
}

As an aside, note that even if you did use an automatic property (e.g. List<EmployeeModel>), that it would assume a default value of null, unless otherwise initialized elsewhere, so nothing changes in this respect.

Edit, Re : What are the benefits?

  • By removing the setter, or making it private, we prevent a caller from reassigning the internal collection of a HumanResource
  • By softening the collection from a List<> to an IEnumerable<>, it means the caller can only do read-only actions on the internal collection, such as to iterate it. In addition, IEnumerable<> can be used in a lazy iteration, allowing the caller to quit enumerating as soon as it has the data it needs.
  • As per the comment below, if the caller requires the data represented in a different collection, such as an Array, then LINQ extension methods such as .ToArray(), .ToList(), .ToDictionary() can be used. Doing so will create new collections for the caller, but with references to the same EmployeeModel objects. The performance penalties of doing this are minimal.

One final note is that there is usually no point in making the setter on an IEnumerable property private, or declaring the backing field as an IEnumerable, as this will prevent the class itself from using impure methods to manipulate the collection (i.e. add or remove objects from it), as doing so would require a cast, e.g.:

public class HumanResource
{
    public IEnumerable<EmployeeModel> Employees
    {
        get; 
        private set;
    }

    public HumanResource()
    {
        // Although the property can be assigned in the CTor to prevent the null issue ...
        Employees = new List<EmployeeModel>();
    }

    private void AddProductiveEmployee()
    {
        // ... We need to continually cast within the class, which is just silly.
        (Employees as IList).Add(new EmployeeModel());
    }

We would have the same problem with the manual backing field approach with an internal IEnumerable<>

// Unlikely to be useful
private readonly IEnumerable<EmployeeModel> _employees = new List<EmployeeModel>();

TL;DR

  • Use a collection type which is appropriate for the internal usage to the class (OO composition)
  • But on the external interfaces (e.g. public getter / property), hide the internal implementation to the minimum necessary (OO encapsulation)
  • Initializing the internal collection in the constructor (or inline) will prevent a null collection being exposed
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • Hi, this I know but my problem is how is the user who will be using the class know what type it returns. For example, in your example, you returned a List. But when an external user uses this class, he knows only that the property return IEnumerable. If he assigns this to a type Array (which also implements IEnumerable), there would be an error. I hope I am asking the right questions. – g_b Dec 31 '14 at 11:54
  • 1
    By providing the `IEnumerable` interface, we are preventing the caller from a) re-assigning and b) adding / removing elements from the internal `_employees` collection. The caller can pull the items into his OWN Array (e.g. `myHumanResource.Employees.ToArray()`) – StuartLC Dec 31 '14 at 11:57