C# from clause vs nested foreach loops

Short story: writing a unit test for an image processing function, I had the following key parameters for each test:

enum Algorithm {  A, B, C }; 
enum ImageFormat {  Gray, Color }; 
enum ImageSize {  Small, Medium, Large };

I wrote a core test function that worked on just one combination of the 3 parameters. E.g.

void Test(
    Algorithm algorithm, 
    ImageFormat imageFormat, 
    ImageSize imageSize)
{
    Console.WriteLine($"Testing algorithm {algorithm} for " +
        {imageFormat} image with {imageSize} size");

    // the test...
}

Next, I wrote a utility to make iterating the values of an enumeration a little easier.. still not sure why this isn’t part of .Net yet:

class EnumHelper<T&gt;
{
    static public T[] Values
    {
        get { return (T[])Enum.GetValues(typeof(T)); }
    }
}

Then, I wrote the nested loops that built each combination and sent them for testing:

void TestAllV1()
{
    foreach (var algorithm in EnumHelper<Algorithm&gt;.Values)
    {
        foreach (var imageFormat in EnumHelper<ImageFormat&gt;.Values)
        {
            foreach (var imageSize in EnumHelper<ImageSize&gt;.Values)
            {
                Test(algorithm, imageFormat, imageSize);
            }
        }
    }
}

Now there’s nothing really wrong with the above, but it looked like something that should be able to be written more simply. So I came up with this:

void TestAllV2()
{
    var tests =
        from algorithm in EnumHelper<Algorithm&gt;.Values
        from imageFormat in EnumHelper<ImageFormat&gt;.Values
        from imageSize in EnumHelper<ImageSize&gt;.Values
        select (algorithm, imageFormat, imageSize);


    foreach (var test in tests)
    {
        Test(test.algorithm, test.imageFormat, test.imageSize);
    }
}

The use of the from clause seems better, mainly due to the reduced level of nesting. The Visual Studio 2019 code analysis metrics are interesting:

MemberMICycC
ClsC
TestAllV1() 76 4 7
TestAllV2()69 2 18

Where:

  • MI: Maintainability Index
  • CycC: Cyclomatic Complexity
  • ClsC: Class coupling

So the foreach approach is (allegedly!) more maintainable, while the from clause method has a lower cyclomatic complexity. This latter metric reinforces the idea that this is slightly simpler than the foreach technique.

It’s also quite easy to add specific filtering inside the tests generator. For example, to quickly stop testing the B algorithm:

var tests =
    from algorithm in EnumHelper<Algorithm&gt;.Values
    where algorithm != Algorithm.B
    from imageFormat in EnumHelper<ImageFormat&gt;.Values
    from imageSize in EnumHelper<ImageSize&gt;.Values
    select (algorithm, imageFormat, imageSize);

Food for thought šŸ™‚

Edit: actually found another way to do this using the Linq SelectMany method, but I’m not keen on this:

void TestAllV3()
{
    var tests =
        EnumHelper<Algorithm&gt;.Values.SelectMany(
            algorithm =&gt; EnumHelper<ImageFormat&gt;.Values.SelectMany(
                imageFormat =&gt; EnumHelper<ImageSize&gt;.Values.Select(
                    imageSize =&gt; (algorithm, imageFormat, imageSize))));

    foreach(var test in tests)
    {
        Test(test.algorithm, test.imageFormat, test.imageSize);
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s