Skip to content

Differences Between Delphi Autofixture and .Net autofixture

Jens Hykkelbjerg edited this page Aug 31, 2020 · 6 revisions

There are quite a few differences between the .Net Autofixture, and the Delphi version. This page explains the differences

Create versus New

In .net the function "Create" is used to create anonymous objects or values. In Delphi there is a convention that the Create function is always a constructor for the object, so keeping the original Create function would not make sense for a Delphi developer or would easily lead to confusion.

This means that the following C# code, used to create an object instance:

Fixture fixture = new Fixture();
TMyObject testObj = fixture.Create<TMyObject>();

Would look like this in Delphi:

var
  vFixture: TAutoFixture;
  testObj: TMyObject;
begin
  vFixture := TAutoFixture.Create;
  try
    testObj := vFixture.New<TMyObject>;
  finally
    FreeAndNil(vFixture)
  end;
end;

It should be obvious from this example, that the functionality of New and Create has switched places between .Net and Delphi. It could be argued that a different name for the create function could be used, but I like the fact that you just have to remember that New and Create has switched places between the .Net and Delphi versions.

CreateMany

In .Net the function CreateMany returns a collection of elements of type T. As usual the delphi version doesn't use "Create", so it should be "New" instead, but "NewMany" might not indicate to the novice user what's going on, so instead I named this function NewList, and it will return a TList

Interfaces

Anyone moving from .Net to Delphi will notice that interfaces are much more difficult to work with in Delphi. You can't create an interface in Delphi without including functions in the interface to handle reference counting. This means you should either implement reference counting yourself or have the class inherit from a class where reference counting is already implemented. Reference counting as such is fine, the problem is that only references to objects that goes through an interface are counted, so mixing class references and interface references is not recommended.

What's worse for Autofixture is that Delphi interfaces cannot contain generic functions. For instance in .Net there is an IFixture interface containing "ICustomizationComposer Build". such an interface is impossible to create in Delphi. Instead the Delphi version of Autofixture uses interfaces where possible, but most of the user facing interface is class based in order to support generic functions.

Customization

In Autofixture you can use the "Build" function to define quick changes to default AutoFixture behaviour, and get a single object using these changed generation rules. However it's also possible to store these changes so you can generate many objects of the given type using these rules. to do this you would use the "Customize" function in the .net version, giving it a lambda expression providing the necessary customizations in the same way as you would normally use the build function.

The delphi version of autofixture currently does not have a "Customize" function (work in progress though), instead you can use the "Configure" function. The configure function has exactly the same interface as Build (In fact behind the scenes it's the same object returned). The only difference is that Configure stores the generated configurations inside autofixture for later use, while the "Build" function only returns the object for one-time use.

Overriding customization

In the .Net version of AutoFixture the default behaviour of a new customization built using the Customize is that it will override any previous customizations. Not so in the delphi version. if you call Configure multiple times in delphi, you will just get a reference to the same TObjectConfig object. This object will of course contain any previous configurations created for this type. If you call configure first, and then Build later, the build object is NOT the same object, but the generation of any properties not specifically initialized inside the build will defer to the previously defined configure.

I see this as an improvement over the .Net version, since it's not obvious why it should not be possible to first configure one property, and later another property of the same class. This also allows you to generate one object, then change the config slightly before generating another.

Exec instead of "Do"

Since "Do" is a reserved word in Delphi, the "Do" function is called "Exec" instead in Delphi. This is relevant for both the Build interface as well as the Configure interface.

lambda expressions versus anonymous functions

The delphi version of autofixture uses anonymous functions whenever the original AutoFixture would have used lamda expressions. This means that the delphi version becomes a lot more "wordy" than the .net version. For inastance this is how a lambda expression looks in .net:

x => x.field

The equivalent anonymous function in delphi cannot be created without specifying the type of the x parameter for the function:

function (x: TMyObject) begin
  Result := x.field;
end

This is a lot more cumbersome to write, and has caused me to write functions accomplishing the same task without the compile time type safety.

Type inference

.Net is pretty good when it comes to inferring the type of generic function, which means that quite often it's not necessary to supply the actual type for generic functions.

For instance in the .net version you can use:

  var myList = new List<int>();
  fixture.AddManyTo(myList);

Without specifying that the version of "AddManyTo" is in fact "AddManyTo<int>". Delphi is pretty bad in comparison, quite often when you think it should be possible for the delphi compiler to infer the type, it will be unable to do so, and you are usually better off simply specifying the type of the generic functions at all times.

Clone this wiki locally