Promise only what you can keep
When I write a method I often try using interfaces as return type and parameters, as long as it's not a method that should return a domain object. The reason for this is that I don't wan't to promis to much to the calling code.
The parameters to your method should be of the lowest type in the inheritence that you can use in the method. For instance, if you have a method that should iterate through a collection of some kind, I would have that parameter be a IEnumerable or if you need the Add() method I would have as a ICollection. This will gain you more flexability as you can call this method with any collection type in the .NET framework if it is a IEnumerable. If it instead was a List there is only one kind of collection that you can call it with and you will loose flexability.
You have to be a little bit more careful about the return type, as you can't be 100% certain of how the calling code will use the return. But I usualy try to find the lowest type that has all the functionality that the calling code is likly to need. This way I promise less to the caller and it will give me less to maintain. A very common example of this is the User/Role relationship. In many application you will have a user object that has a list of roles that he/she is associated with. This can look like this:
public class User
{
.....
public List<Role> Roles { get; set; }
}
This code promises alot, and the programer that use this type can do very much with it that you might not want him/her to be able to do. There is the ability to replace the whole collection (public setter) and you return a very specific collection type wich has a lot of functionality. I would rather write it like this:
public class User
{
.....
public IEnumerable<Role> Roles { get; private set; }
}
Here I have replaced the public setter with a private and changed the return type to be IEnumerable<Role>. This way I protect the collection from beeing reset by the calling code, which you rarely wan't, and I don't expose a Add() method to the collection.
But what If I wan't to use the class User as a agregate root and be able to add roles to it? Well, I usually have two solutions for this. Eather you can simply return a ICollection<Role> instead of IEnumerable<Role> which will give the calling code a Add() method to the roles collection. The other option is the one I would prefer, that is to add a AddRole() method to the User class. That can look like this:
public class User
{
.....
private IList<Role> _roles;
public IEnumerable<Role> Roles
{
get { return _roles; }
private set { _roles = value; }
}
public void AddRole(Role role)
{
_roles.Add(role);
}
}
This way you have limited the functionality of the roles collection to readonly, while still having the ability to add a role to the user. This also enables you to replace the list collection here with any other collection type in the .Net framework.
Whilst I completely agree with and implement the principle of providing the minimum level of functionality and accepting and returning an interface rather than a concrete class, in your example of using IEnumerable for the collection property I was wondering if you have tried that when using NHibernate?
I have various places in my object model (for which we are using NHibernate for ORM) where I have additional logic in my "add" method but NHibernate requires that you provide collections as an IList so although you can tell the rest of your team to use the add method, they do sometimes forget and do IList.Add instead which can cause issues. I know that code reviews can pick up on this kind of thing but it would be nice if the object model didn't allow the mistake to occur in the first place.
I use NHibernate for just about all my projects and I usually use IEnumerable as my collection type. I have not had a problem with that. I guess it depends a little bit on how you map it, but if you map it as a bag it should not cause you any problem.