The Problem
I had to solve a problem a while back at work where I needed to call the same method multiple times but return different values in a known order. Now on the face of this it doesn’t seem too bad however I had complete brain fog and reached out on Twitter. Dave Glick took the time to reply and it started to trigger ideas. Thanks Dave!
Let’s explore some of the options I came up with.
Option 1
First option was to increment a counter and index through an array.
public class MyService
{
private string[] _results = {"One", "Two", "Three"};
private int _count = 0;
public string GetNext()
{
return _results[_count++];
}
}
This solves the problem. It’s pretty basic. There is however no error handling or bound checking which isn’t ideal!
Option 2
So how do we solve this issue? Using an enumerator could be used but how? I didn’t want to expose the inner workings to the caller, but it didn’t mean I couldn’t use it internally, right?
public class MyService2
{
private string[] _results = { "One", "Two", "Three" };
private IEnumerator<string> _enumerator;
public string GetNext()
{
if (_enumerator == null)
{
_enumerator = _results.AsEnumerable().GetEnumerator();
}
if (_enumerator.MoveNext())
{
return _enumerator.Current;
}
throw new Exception("Got to the end.");
}
}
This would iterate over the items in _results
on every call and return the current value when called. Solved the issue. Very explicit but also seemed a bit verbose.
Option 3
The use of the enumerator is fine. And the converting from the string[]
into an enumerator is fine. However taking into account the verbosity of the previous solution I applied some refactoring.
I first started off by creating the IEnumerator<string>
up front. Taking out the enumerator creation in the method allowed for the logic flow to be converted into an inline conditional expression which was returned directly from an expression bodied method.
public class MyService3
{
private readonly IEnumerator<string> _enumerator = new []{ "One", "Two", "Three" }.AsEnumerable().GetEnumerator();
public string GetNext() => _enumerator.MoveNext() ? _enumerator.Current : throw new Exception("Got to the end.");
}
It’s clean but I would argue that it’s probably less readable by this point.
Testing
But how did I test this? The structure of the unit test to prove this worked was along the lines of the below with swapping out the service implementation.
[Fact]
public void Test3()
{
// Arrange
var sut = new MyService3();
// Act / Assert
var item = sut.GetNext();
item.Should().Be("One");
item = sut.GetNext();
item.Should().Be("Two");
item = sut.GetNext();
item.Should().Be("Three");
Action action = () => sut.GetNext();
action.Should().ThrowExactly<Exception>().Which.Message.Should().Be("Got to the end.");
}
Conclusion
In this post we have explored a few ways to solve the intial problem. I actually found a different solution to the problem and didn’t need it in the end but thought it would be fun to write it up anyway :-)
Any questions/comments then please contact me on Twitter @WestDiscGolf