Wiki

Clone wiki

Spring4D / Spring4D 2.0 Releasenotes

Spring4D 2.0 Release notes

2.0 contains a ton of bugfixes – they are not listed individually – if you reported any and they are not open anymore on bitbucket they have been fixed.

Below are the most notable changes in the library, specific breaking changes, and some technical explanations about some changes.

Support for Delphi 2010 has been dropped in 2.0 as well as support for any platform that used ARC - that means any mobile platform and Linux are only supported from Delphi 10.4 on.

Spring.Base

various changes (not listing every single one but the most important ones):

  • Specification pattern has moved to its own unit Spring.Patterns.Specification

  • Internal refactoring of the multicast event to provide high performance when operating in high congestion (before internally they used a critical section and caused a copy of the handlers), now it uses the so-called hazard era memory reclamation model.

  • Removed Invoke method from IEvent<T> - it usually is not good practice to invoke an event from the outside but only from within the type that provides an event – however there are situations where it still is needed – for that there is IInvokableEvent<T> or the INVOKABLE_EVENT define to add the Invoke method back which for example DSharp needs (and tells you about if you compile)

  • Introduction of method reference types that have const parameters (opposed to those from System.SysUtils): previously called TAction<...> is now called Action<...> (yes, without a leading T, these are the counterpart to TProc<...>) and introduce Func<...> which are the counterpart to TFunc<...> - these are almost exclusively used throughout the entire library. So it is very likely that you will get some compiler error which might be confusing because it might say something like E2250 There is no overloaded version ofWherethat can be called with these arguments - if you see these check if the mentioned method is one of those that take a method reference (aka anonymous method) which is missing a const.

Spring.Collections

Some terms first:

  • Sequence – references to an IEnumerable<T> meaning a sequence of elements which are not guaranteed to be materialized

  • Materialized collection – a collection that has its own storage such as a list or a dictionary – opposed to a non-materialized sequence such as the result of the Where method which returns an IEnumerable<T> which can be operated on, but which is deferred executed and does not have its own storage of values.

  • Deferred execution – most sequences that are being returned from IEnumerable<T> methods that return IEnumerable<T> as well such as Where, Skip or Take.

Complete overhaul of collection types – removed all dependencies on the RTL except of the interfaces IComparer<T> and IEqualityComparer<T> from System.Generics.Defaults (but only the interfaces, the implementations are all implemented in Spring.Comparers)

Introduced TPair<TKey,TValue> into Spring.Collections - this enables being completely independent from System.Generics.Collections. It is recommended to use as much from Spring.Collections as possible and if necessary, add System.Generics.Collections before Spring.Collections in the uses to avoid any necessity of fully qualifying anything.

Removed most non-generic collection types such as IList, IDictionary, IStack, IQueue and ISet – also IEnumerable<T> and IEnumerator<T> do not inherit from their respective non-generic counterparts anymore (keep in mind that these interfaces are not compatible with those similarly named ones from System) - it is still possible though to query any collection type as the non-generic IEnumerable – it internally then creates a wrapper instance that performs the generic to TValue transformation.

Removal of the Spring.Collections.Adapters unit (this contained the adapters from generic to non-generic collection interfaces, the only adapter needed for ICollection support resides in Spring.Collections.Base and is internally created anyway)

Removal of the Spring.Collections.Enumerable unit – it was an experiment to begin with albeit a not very successful one (the fact that the type was a record caused quite some trouble on the compiler when used extensively – most if not all functionality can be found either on IEnumerable<T> (see below for detailed changes) or as static methods on Spring.Collections.TEnumerable although the syntax might be a little different.

Turned off enhanced RTTI for implementing classes and interfaces – interfaces also do not inherit from IInvokable anymore. For serialization most collections can be queried for the non-generic ICollection interface which provides for in support for serialization and the Add method for deserialization. Also added some factory methods to create IList<T> (where T is a class or interface) in a non-generic way which while still being able to provide the exact element type which helps in serialization code and does not require any RTTI.

Added lots of new methods to IEnumerable<T> - a brief listing below – see the xmldoc for more details:

  • GetNonEnumeratedCount – this mostly is provided for internal use of other methods listed next

  • Count based functions: AtLeast, AtMost, Between and Exactly – it is recommended to use those when dealing with IEnumerable<T> to avoid unnecessary enumeration of non-materialized sequences – better use e.AtLeast(2) rather than e.Count >= 2 because AtLeast only needs to iterate for up to 2 elements at most while Count iterates all items which can be quite costly.

  • More aggregation methods/overloads: Average, Max, Min, Sum

  • New method DefaultIfEmpty which returns itself or a sequence with only one element which is the default for the element type or the provided value if the sequence is empty. It can be handy when applying functions on a sequence that will raise if the sequence is empty.

  • Following methods that were only available via TEnumerable have been added directly added to IEnumerable<T>: Distinct, Exclude (was called Except on TEnumerable, this name change has been made to avoid conflict with the except keyword), Intersect and Union

  • Memoize which causes a non-materialized deferred executed sequence to be materialized only once – for example iterating a sequence that was the result of the Where method twice will also execute the predicate passed to the Where method twice for each element – calling Memoize on the result of Where will only materialize the sequence once and cache the already materialized items. This also works on partially enumerated sequences.

Massive refactoring of the implementing classes – please keep in mind that these are always meant to be an implementation detail and thus you will possibly face severe breaking changes.

These refactorings have been done to achieve multiple goals:

  • improve performance: almost all former virtual methods have been removed because especially on win32 virtual methods behind interfaces suffer from a compiler issue (see RSP-18108)

  • reduce instance size: interfaces are almost always implemented only on one single class and not throughout the inheritance chain – this goes with the previously mentioned change because if a class does not implement some interface method yet (previously it then was virtual abstract) the interface cannot be implemented on that class. Also implementing interfaces that inherit from each other share the same IMT slot in an instance (every object instance basically has invisible fields for every interface it implements which increases every instance size). Example: Previously TEnumerableBase<T> implemented IEnumerable<T>, TCollectionBase<T> inherited from that and implemented ICollection<T> and then TListBase<T> implemented IList<T> so an instance of such an object had three of those hidden fields for each of their interfaces. In 2.0 only TList<T> implements (among some others) the interfaces IEnumerable<T>, ICollection<T> and IList<T> - since those three interfaces inherit from each other the compiler (for once) is smart enough to store them all in one hidden field (basically the one for IList<T> because it covers the other two due to interface inheritance)

  • splitting some internal classes into non-generic parts to reduce binary code bloat – the parts that the compiler emits for the different generic type parameters. And combining similar patterns into the same types. Especially associative collection types such as dictionaries are heavily affected by those as they have two generic type parameters which increases the possible combinations and overlappings that can be reduced by splitting their internal parts into different types.

  • almost all enumerators are not implemented by classes but handcrafted to minimize call overhead for MoveNext and GetCurrent – implementing interfaces via classes means every method goes through their adjustor thunks which had a noticeable impact in loops. Yes, we still have interfaces as iterators so there is no inlining which would make it faster, but we get the best that we can get.

Implementation of all hashtable-based collections (dictionary, set, and multimap) are now using an implementation that stores items in insertion order – previously these collections had dedicated Sorted... versions, these do not exist anymore since dictionary, set, and multimap are ordered default (not to be confused with the Ordered... versions, these are implemented via a red-black tree and thus store their items by the ordering determined by the used IComparer<T>)

Introduced a lot of new factory methods to TEnumerable to create various existing and new collection types such as:

  • bounded deque, queue, and stack: those have a fixed capacity, they do not grow but refuse elements beyond their capacity

  • evicting deque and queue: those also have a fixed capacity but adding elements past their capacity removes elements on their other side

  • various new versions of multimaps:

    • Multimap is the default, basically like a dictionary<key, list\<value>>

    • HashMultiMap, in this the values are sets so every item can only occur once, and they retain the insertion order (see above the paragraph about the hashtable)

    • TreeMultiMap, this time the values are organized by a sorted set (a red-black tree), so again per key there can only be unique values but additionally, they are organized by their sorting order (determined by the IComparer<…>)

    • Each of the previous 3 multimaps also has a Sorted... version – while the previous ones are like a dictionary where the keys are organized by a hashtable (again retaining insertion order) the Sorted... versions have a red-black tree organizing the order of the keys. So, for example, a SortedTreeMultiMap internally is a red-black tree where for each key there is another red-black tree that contains the values per key.

  • New Multiset which acts like a Dictionary<T,Integer> that provides an easy way to keep unique counts for certain elements. You just add or remove from a MultiSet<T> and it performs the counting.

Due to the fact that the hashtable-based collections are ordered by default CreateDictionary now returns IOrderedDictionary (which inherits from IDictionary so any existing code that used CreateDictionary before should still work) - however, if you used CreateOrderedDictionary before you now need to use CreateDictionary. Similar goes for creating sets.

The order of some parameters on a few of the collection factory methods has been changed to be consistent with other overloads – a compile error will point those out.

Spring.Core

Spring.Container

Eliminated a severe case of compiler slowdown due to extensive use of generic record type (TRegistration<T>) which only needed to be generic for one specific method: DelegateTo

This method has been removed and replaced by an overload of RegisterType that directly takes the delegate. Thus, the result type for the fluent API does not need to be generic anymore because further methods do not need to carry the registered type as generic part.

Some internal refactorings that only affect any customizations to the container.

Removed IKernel type and pass around TKernel reference instead – that avoided some unnecessary reference counting overhead and possible circular references throughout the fluent API.

Implemented custom equality-comparer for PTypeInfo to be able to resolve identical types across binary boundaries (this is especially the case for generic types but also useful for interfaces) that is being used inside the container – this enables resolving identical types (actually meaning the same type but it can reside in different binaries and thus have a different PTypeInfo pointer) because the comparer considers the TTypeKind and the full qualified name on those types.

Added InjectParameter to registration to be able to provide individual parameters for the ctor of the registered types which before was only in a very weird way using InjectConstructor and easily broke when for example adding a parameter to the ctor.

Spring.Mocking

Added possibility to easily mock methods with by ref parameters using Arg.Ref - more detailed documentation tbd – see the unit tests in Spring.Tests.Mocking in the meanwhile

Implemented ability to initialize default mock behavior from an existing implementation

Updated