I'm developing a library intended for public release. It contains various methods for operating on sets of objects - generating, inspecting, partitioning and projecting the sets into new forms. In case it's relevant, it's a C# class library containing LINQ-style extensions on IEnumerable
, to be released as a NuGet package.
Some of the methods in this library can be given unsatisfiable input parameters. For example, in the combinatoric methods, there is a method to generate all sets of n items that can be constructed from a source set of m items. For example, given the set:
1, 2, 3, 4, 5
and asking for combinations of 2 would produce:
1, 2
1, 3
1, 4
etc...
5, 3
5, 4
Now, it's obviously possible to ask for something that can't be done, like giving it a set of 3 items and then asking for combinations of 4 items while setting the option that says it can only use each item once.
In this scenario, each parameter is individually valid:
- The source collection is not null, and does contain items
- The requested size of combinations is a positive nonzero integer
- The requested mode (use each item only once) is a valid choice
However, the state of the parameters when taken together causes problems.
In this scenario, would you expect the method to throw an exception (eg. InvalidOperationException
), or return an empty collection? Either seems valid to me:
- You can't produce combinations of n items from a set of m items where n > m if you're only allowed to use each item once, so this operation can be deemed impossible, hence
InvalidOperationException
. - The set of combinations of size n that can be produced from m items when n > m is an empty set; no combinations can be produced.
The argument for an empty set
My first concern is that an exception prevents idiomatic LINQ-style chaining of methods when you're dealing with datasets that may have unknown size. In other words, you might want to do something like this:
var result = someInputSet
.CombinationsOf(4, CombinationsGenerationMode.Distinct)
.Select(combo => /* do some operation to a combination */)
.ToList();
If your input set is of variable size, this code's behaviour is unpredictable. If .CombinationsOf()
throws an exception when someInputSet
has fewer than 4 elements, then this code will sometimes fail at runtime without some pre-checking. In the above example this checking is trivial, but if you're calling it halfway down a longer chain of LINQ then this might get tedious. If it returns an empty set, then result
will be empty, which you may be perfectly happy with.
The argument for an exception
My second concern is that returning an empty set might hide problems - if you're calling this method halfway down a chain of LINQ and it quietly returns an empty set, then you may run into issues some steps later, or find yourself with an empty result set, and it may not be obvious how that happened given that you definitely had something in the input set.
What would you expect, and what's your argument for it?