Tag Archives: C#

C#

List Builder Pattern – Unit Testing Object Hierarchies in C#

When coding processing-intensive tasks, my classes are typically immutable and data-only (similar to F# records without structural equality). I then create classes that take the inputs through the constructor and transform the data with a single public method call.

In order to unit test this code, I need to be able to quickly generate dummy test data. At this point I usually turn to the builder pattern.

Sample Application

In the following examples we have a simple data hierarchy of a CAD assembly. An assembly has a name, ID, and a list of parts. Each part has a name, ID, mass, and quantity.

public class Assembly
{
	public Guid Id { get; private set; }
	public string Name { get; private set; }
	public IList<Part> Parts { get; private set; }

	public Assembly(Guid id, string name, IList<Part> parts)
	{
		Id = id;
		Name = name;
		Parts = parts;
	}
}

public class Part
{
	public Guid Id { get; private set; }
	public string Name { get; private set; }
	public int Quantity { get; private set; }
	public double Mass { get; private set; }

	public Part(Guid id, string name, int quantity, double mass)
	{
		Id = id;
		Name = name;
		Quantity = quantity;
		Mass = mass;
	}
}

After reading assembly data from a spreadsheet or some other user input, we need to validate it before inserting it into the database. We will unit test the following rules.

  • Assembly names must be unique.
  • Part names must be unique within and across assemblies.

Builder Pattern

The standard unit test builder pattern:

  1. Establishes default values for all fields
  2. Provides public setters to override the defaults – typically using a fluent style interface.
  3. Contains a Build() method (or an implicit conversion) which calls the constructor to build the type under test.

An assembly builder would look like this.

 
public class AssemblyBuilder
{
	private Guid _id = Guid.Parse("b5f6f602-f123-4730-b1b2-9c246f4807e8");
	private string _name = "Assembly 1";
	private IList<Part> _parts = new List<Part>();

	public AssemblyBuilder Id(Guid id)
	{
		_id = id;
		return this;
	}

	public AssemblyBuilder Name(string name)
	{
		_name = name;
		return this;
	}

	public AssemblyBuilder Parts(IList&lt;Part&gt; parts)
	{
		_parts = parts;
		return this;
	}

	public Assembly Build()
	{
		return new Assembly(_id, _name, _parts);
	}
}

A unit test for assembly name uniqueness would look like:

public void Validate_DuplicateAssemblyNames_Error()
{
	var assembly1 = new AssemblyBuilder()
					.Name("Wing").Build();

	var assembly2 = new AssemblyBuilder()
					.Name("Wing").Build();

	var validator = new AssemblyValidator(new[] { assembly1, assembly2 });
	var error = validator.Validate();

	Assert.Contains("duplicate names", error);
}

There are a couple issues with this approach already.

1. When making several assembly instances the default behavior is to create the same exact assembly. What’d we like is for each assembly be different but valid. For Guid fields all we need is a call to Guid.NewGuid(). For other fields we will need to make some helper methods.

2. It’s tedious to call multiple builders to make a list. We want something like NBuilder.

var assemblies = Builder<Assembly>CreateListOfSize(2)
					.TheFirst(2).With(a => a.Name = "Wing").Build();

However we can’t use NBuilder here because the class fields do not have public setters and there is no default constructor. NBuilder also nulls the Parts field by default.

3. We’ve completely avoided the parts collection in this implementation. This goes back to point #1 where we’d like to generate a list of unique and valid objects.

List Builder Pattern

Instead of the builder generating a single instance, we will design it to generate a list of objects. We will also use helper methods to generate random, valid test data – à la FsCheck.

Here is the PartsBuilder. The size of the list is passed in via the constructor. Given the size, a list of random data values is given for each field. The *Generator classes are simple helper classes to generate each field array. The Build() method simply loops over the field arrays and generates a list of parts.

public class PartsBuilder
{
	private int _size;
	private IList<Guid> _ids;
	private IList<string> _names;
	private IList<int> _quantities;
	private IList<double> _masses;
.
	public PartsBuilder(int size)
	{
		_size = size;
		_ids = new GuidsGenerator(size: size).Generate();
		_names = new OrderedStringGenerator(size, basename: "Part").Generate();
		_quantities = new RandomIntegerGenerator(size, min: 1, max: 999).Generate();
		_masses = new RandomDoubleGenerator(size, min: Double.Epsilon, max: Double.MaxValue).Generate();
	}

	public PartsBuilder Ids(params Guid[] ids)
	{
		_ids = ids;
		return this;
	}
	
	public PartsBuilder Names(params string[] names)
	{
		_names = names;
		return this;
	}
	
		
	public PartsBuilder Quantities(params int[] quantities)
	{
		_quantities = quantities;
		return this;
	}
	
	public PartsBuilder Masses(params double[] masses)
	{
		_masses = masses;
		return this;
	}
	
	public IList<Part> Build()
	{
		var parts = new List<Part>();
		for (int i = 0; i < _size; i++)
		{
			var part = new Part(_ids[i], _names[i], _quantities[i], _masses[i]);
			parts.Add(part);
		}

		return parts;
	}
}

The assembly builder follows the same pattern. Notice that we are now generating valid parts data. By default each assembly will have 1-10 valid parts assigned.

public class AssembliesBuilder
{
	private int _size;
	private IList<Guid> _ids;
	private IList<string> _names;
	private IList<PartsBuilder> _parts;

	public AssembliesBuilder(int size)
	{
		_size = size;
		_ids = new GuidsGenerator(size).Generate();
		_names = new OrderedStringGenerator(size, basename: "Assembly").Generate();
		_parts = Enumerable.Range(0, size).Select(i => new PartsBuilder(Gen.Int(1, 10))).ToList();
	}

	public AssembliesBuilder Id(IList<Guid> ids)
	{
		_ids = ids;
		return this;
	}

	public AssembliesBuilder Names(params string[] names)
	{
		_names = names;
		return this;
	}

	public AssembliesBuilder Parts(params PartsBuilder[] parts)
	{
		_parts = parts;
		return this;
	}

	public IList<Assembly> Build()
	{
		var assemblies = new List<Assembly>();
		for (int i = 0; i < _size; i++)
		{
			var assembly = new Assembly(_ids[i], _names[i], _parts[i].Build());
			assemblies.Add(assembly);
		}
		return assemblies;
	}
}

Our test code to detect duplicate assembly names is now similar to NBuilder. The main difference is that when overriding fields – each field in the list must be defined.

public void Validate_DuplicateAssemblyNames_Error()
{
	var assemblies = new AssembliesBuilder(size: 4)
		.Names("Wing", "Engine", "Wing", "Flaps")
		.Build();

	var validator = new AssemblyValidator(assemblies);
	var error = validator.Validate();

	Assert.Contains("duplicate names", error); // Wing
}

In our final example, we want to test duplicate part names across assemblies. Here we generate two assemblies and override the PartsBuilder for each one.

public void Validate_DuplicatePartNamesAcrossAssemblies_Error()
{
	var assemblies = new AssembliesBuilder(size: 2)
		.Parts(new PartsBuilder(size: 4)
				   .Names("Beam", "Bracket", "Widget", "Panel"),
			   new PartsBuilder(size: 2)
				   .Names("Widget", "Rivet")
		).Build();

	var validator = new AssemblyValidator(assemblies);
	var error = validator.Validate();

	Assert.Contains("duplicate names", error); // Widget
}

How to Call Fortran DLLs from C#

C# has direct support for calling Fortran via DLLs using P/Invoke. In C#, simply define a static method with the same method signature as the Fortran method. No code generation tools are needed.

Using Fortran with a high-level language like C# allows you to leverage the strengths of both languages. C# can be used to create a GUI as well as preprocess data from files, database, or other applications. This leaves Fortran to focus solely on the number crunching.

Here are some general rules for calling Fortran from C#.

  • The Fortran DLL should reside in the same directory as the C# assembly (DLL or EXE). Automatically copy the Fortran DLL using post-build events.
  • Array indices are switched between Fortran and C#. Fortran and C# arrays are stored column and row major order respectively.
  • Fortran derived types map to C# structs. Be sure to explicitly define the alignment. The default alignment in Intel Fortran is 8 bytes.
  • Scalar values in Fortran are passed by reference. The equivalent C# scalar should be passed by reference (ref) as well. Failing to do so will usually cause memory access violations.
  • C# strings should be converted to character arrays padded with spaces.

Solution Setup

The Visual Studio solution contains and Fortran dynamic link library project and a C# console application. In order for the C# console application to call the Fortran DLL, it must be in the same directory as the C# executable. Other configurations are possible, but are not recommended due to the potential for chaos (DLL hell).

Use post-build events to automatically copy the Fortran DLL to the C# build directory as shown below. Here we use some of the Visual Studio macros. Remember to include the quotes if you solution is located in a path with spaces.
copy "$(OutDir)\$(TargetName).dll" "$(SolutionDir)\CSharpApp\bin\Debug"
copy "$(OutDir)\$(TargetName).dll" "$(SolutionDir)\CSharpApp\bin\Release"

post-build_fortran

Passing Arrays

The following example shows how to pass arrays. The Fortran subroutine translates an array of coordinates by the vector delta.

coords is a 3 x n array where n is the number of coordinates. n is passed into the subroutine.

Several compiler directives are used to export the subroutine to the public interface of the DLL. The ATTRIBUTES DLLEXPORT directive exports the subroutine. The ATTRIBUTES ALIAS overrides any compiler specific name mangling. Compiler directives are prepended by !DIR$, !DEC$, or !MS$.

subroutine TranslateViaArrays(delta, n, coords)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaArrays
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaArrays' :: TranslateViaArrays

    real(8), intent(in) :: delta(3)
    integer, intent(in) :: n
    real(8), intent(inout) :: coords(3,n)

    integer :: i

    do i = 1, n
        coords(:,i) = coords(:,i) + delta(:)
    end do

end subroutine

The calling code in C# wraps the DLL call in a static class call FortranLib. The name of the DLL is defined in a private field.

Within the class a static extern method is defined with the [DllImmport] attribute. The DllImport attribute defines how the DLL is called – most importantly the DLL name, calling convention, and the character set.

The signature of the method matches the Fortran version with some extra attributes to describe how each argument is marshalled from C# to Fortran and back.

  • The scalar argument n is passed by reference (ref) as this is the Fortran convention.
  • The array arguments do not use the ref keyword since arrays are reference types in C# (as opposed to scalar primitives like n which are value types).
  • The array arguments are annotated with the [In] and [Out] attributes. These should match the intent of the Fortran version.
  • The default calling convention for Intel Fortran is C (cdecl).
public static class FortranLib
{
    private const string _dllName = "FortranLib.dll";

    public const int PathLength = 256;

    [DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void TranslateViaArrays([In] double[] delta, ref int n, [In, Out] double[,] coords);
}

The snippet below shows the C# calling code. Notice that the array indices in the coords are reversed between C# and Fortran. The rest is standard C#.

static void RunTranslateViaArrays()
{
    var delta = new[] { 1.0, 2.0, 3.0 };
    int n = 10;
    var coords = new double[n, 3]; // Note that the indices are reversed from Fortran.
    for (int i = 0; i < n; i++)
    {
        coords[i, 0] = i;
        coords[i, 1] = i + 1;
        coords[i, 2] = -i;
    }

    FortranLib.TranslateViaArrays(delta, ref n, coords);

    for (int i = 0; i < n; i++)
        Console.WriteLine("Point {0}: ({1}, {2}, {3})", i + 1, coords[i, 0], coords[i, 1], coords[i, 2]);
}

Passing Types

The following Fortran code performs the same operation as before, except now the point coordinates are encapsulated in a derived type PointType. This derived type will map to an equivalent C# struct.

The bind(c) attribute dictates that the memory layout should be the same as in C – sequential with padding as necessary. The default padding in Intel Fortran is 8 bytes (/align:rec8byte). In PointType a padding integer is placed after the Id field so that the double precision coordinate fields start on the subsequent 8-byte boundary. You can confirm this at run-time using the inquire statement (shown below).

type, bind(c) :: PointType
    integer :: Id
    real(8) :: Coords(3)
end type

subroutine TranslateViaTypes(delta, n, points)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaTypes
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaTypes' :: TranslateViaTypes

    real(8), intent(in) :: delta(3)
    integer, intent(in) :: n
    type(PointType), intent(inout), target :: points(n)

    integer :: i, words
    type(PointType), pointer :: point

    inquire(iolength=words) point
    print *, words ! Confirm the size of PointType. Id(1) + Pad(1) + Coords(3*2) = 8 words. 1 word = 4 bytes.

    do i = 1, n
        point => points(i) ! Use a local (pointer) variable to avoid writing points(i) multiple times.
        point%Coords(:) = point%Coords(:) + delta(:)
    end do    

end subroutine

The equivalent C# struct is shown in the code below:

  • The StructLayout attribute dictates that the memory layout is sequential (like bind(c)). The Pack option specifies a padding of 8 bytes.
  • The coords array has a special MarshalAs attribute which tells C# to reserve space for 3 doubles. C# does not have native support for fixed size arrays (essentially all C# arrays are like Fortran allocatable arrays). C# does have a notion of a fixed array which requires an unsafe code block which I have not tried.
  • The downside of the current approach is that if the struct is created with the default constructor (i.e. points = new Points[10]), it is in an unsafe state since the Coords field is null (high potential for NullReferenceExceptions).
  • The sample code has examples of other implementations.
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct Point
{
    public int Id;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public double[] Coords;

    public Point(int id, double x, double y, double z)
    {
        Id = id;
        Coords = new double[3] { x, y, z };
    }
}

The P/Invoke signature of the C# call is practically the same as the previous example. Switching from raw arrays to types helps make the code self documenting.

[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void TranslateViaTypes([In] double[] delta, ref int n, [In, Out] Node[] nodes);

static void RunTranslateViaTypes()
{
    var delta = new[] { 1.0, 2.0, 3.0 };
    int n = 10;
    var points = new Point[n];
    for (int i = 0; i < n; i++)
        points[i] = new Point(id: i + 1, x: i, y: i + 1, z: -i);

    FortranLib.TranslateViaTypes(delta, ref n, points);

    for (int i = 0; i < n; i++)
    {
        var point = points[i];
        Console.WriteLine("Point {0}: ({1}, {2}, {3})", point.Id, point.Coords[0], point.Coords[1], point.Coords[2]);
    }
}

Passing Strings

Passing strings to Fortran requires that you convert the C# string to a fixed length character array. The following Fortran code shows an example use case where the path to an input file is passed to the DLL subroutine.

subroutine TranslateViaInputFile(inputPath)

    !DIR$ ATTRIBUTES DLLEXPORT :: TranslateViaInputFile
    !DIR$ ATTRIBUTES ALIAS: 'TranslateViaInputFile' :: TranslateViaInputFile

    character(len=LEN_PATH), intent(in) :: inputPath

    print *, "Input File: " // trim(inputPath)

    ! Do something amazing....

end subroutine

The P/Invoke call is similar to the ones before.

[DllImport(_dllName, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void TranslateViaInputFile([In] char[] inputPath);

When converting C# strings to character arrays you must ensure that: 1) the array has the correct length and 2) leftover characters are written as whitespace. The following extension method handles this conversion.

public static char[] ToCharacterArrayFortran(this string source, int length)
{
    var chars = new char[length];
    int sourceLength = source.Length;
    for (int i = 0; i < length; i++)
    {
        if (i < sourceLength)
            chars[i] = source[i];
        else
            chars[i] = ' '; // Important that these are blank for Fortran compatibility.
    }
    return chars;
}

The calling code uses the extension method to create the character array to pass into Fortran.

static void RunTranslateViaInputFile()
{
    const string inputPath = @"C:\temp\input.txt";

    // Convert string to Fortran array of characters.
    char[] inputPathChars = inputPath.ToCharacterArrayFortran(FortranLib.PathLength);

    FortranLib.TranslateViaInputFile(inputPathChars);
}

Debugging

To debug Fortran from the C# application you must activate the unmanaged code debugging in the C# project. Project | Properties | Debug | Enable unmanaged code debugging. Once this option is enabled, you can step directly into the Fortran DLL from the C# application.

enable_unmanaged_debugging_fortran

Sample Code

The link below contains sample code using Visual Studio 2010 and Intel Fortran.
CSharpFortranInterop.zip