I am working with a 3th party library which has following interface defined for a processing task:
public interface IProcessor {
void Execute( FileContent fileContent );
}
Such an IProcessor
gives me the possibility to change the FileContent
before it gets written to the database. For legacy reasons, and for integration reasons with an unnamed ERP, its unique identifiers are string identifiers which have a limitation in length, and they are padded on the left with padding char '0'
.
Names will therefore always have the form of 00100
or 02301
or 10179
for a 5 digit identifier.
Sometimes, we need to add new content inside the fileContent.Components
, but we are not allowed to change the existing identifiers. Theoretically any number of items could be added to the components, but for the question at hand, I simplified it to just adding 20 items.
The existing identifiers do not have any order to them, nor can I be sure that the highest number inside the string would be lower than the theoretical maximum number for this identifier.
To handle this addition, I thought I could use a IEnumerator<string>
function (not class) as an iterator, that creates the next number starting from the theoretical minimum value to the theoretical maximum value, and throw when it exceeds the maximum value.
public static class GapHelper {
public static IEnumerator<string> GetNextName( int maxSize, IEnumerable<string> items, Func<int, string> transform ) {
var maxLength = (long)Math.Pow( maxSize, 10 );
var hashSet = new HashSet<string>( items );
for (var i = 0; i < maxLength; i++) {
var text = transform(i);
if (hashSet.Contains( text )) {
continue;
}
yield return text;
}
throw new ArgumentOutOfRangeException( "Iteration exceeded maximum numbers of items at " + maxSize );
}
public static IEnumerator<string> GetNextName( int maxSize, IEnumerable<string> items) {
return GetNextName( maxSize, items, i => i.ToString().PadLeft( maxSize, '0' ) );
}
}
and this would be used inside such an IProcessor
in the following way:
public class ProcessingTask : IProcessor {
public void Execute( FileContent fileContent ) {
using (var nameGenerator = GapHelper.GetNextName( 5, fileContent.Components.Select( c => c.Name ) ) ) {
for (var i = 0; i < 20; i++) {
nameGenerator.MoveNext();
fileContent.Components.Add( new Component {
Name = nameGenerator.Current, Content = "Foo"
} );
}
}
}
}
What I cannot change:
- How the processor is called, and with which parameters
- How the file is processed afterwards, and might have been processed before
- My bracket positioning, we have them at the end of each line
- I am aware I didn't add documentation to the methods
A full simplified example can be found in this dotnetfiddle.
I am interested in how this code looks from maintainability perspective, and what other potential options I might have overlooked. I am also curious if having an IEnumerator<string>
implemented as a method rather than a class makes a big difference, either for this implementation or from an iterator perspective.
n
number of items or can have. In other words do you add fake items to the result as you are doing here or do you stop when there are no more items to add. \$\endgroup\$