Spring4D is an open-source code library for Embarcadero Delphi 2010 and higher. It consists of a number of different modules that contain a base class library (common types, interface based collection types, reflection extensions) and a dependency injection framework. It uses the Apache License 2.0.
Spring4D is available on GitHub.
Library adds support for nullable types. Nullable types are the types which can have some value of a certain known type or have Null value. You can also assign Null to the variables of this type.
uses Spring, System.Variants; var X: Nullable<Integer>; Y: Integer; begin X := Null; Writeln(X.HasValue); // Outputs False X := 10; Writeln(X.Value); // Outputs 10 Writeln(X); // [Error] E2054 Illegal type in Write/Writeln statement X := Null; Y := X; //Raises EInvalidOpException as nullable has no value end.
Shared type allows you to forget about releasing objects manually. This type wraps the type with reference counting and finalizes the value when the instance of Shared<> type goes out of scope.
uses Spring; var L, R: Shared<TStrings>; begin L := TStringList.Create; R := TStringList.Create; ...do something with them... RemoveLeftStringsFromRight(L, R); R.Value.Text end;
And you do not need to do try/finally to release the objects as they will be automatically destroyed once the Shared<> instance goes out of scope.
Library not only introduces the interface based lists, dictionaries, maps, stacks and queues. But also extends the comfort of using them in comparison with System.Generics.Collections. So if we talk about list implementation we all know from standard library TList<T>. The Spring defines the interfaces IList<T> which adds some very nice extra functions:
- IsEmpty – to check if the list is empty;
- FirstOrDefault, LastOrDefault – to return the first element or Default(T);
- AsReadonlyList – returns the interfaces for read-only access to the list;
- All, Any, ForEach – function allowing you to process the elements with anonymous function callback!
First two additions are just for the comfort, but the AsReadOnlyList is very important when you build your architecture and define the responsibilities. Say if your dependant component just needs to display items from the list why giving it the full access to the list data as we usually are forced to do with standard generics? When you have such a requirement then it’s much better practice to have a dependency property/parameter of type IReadonlyList<T> which can be passed using the AsReadonlyList method.
Using Any, All and ForEach are also very comfortable! If you want to check if any of the elements in your list matches certain criteria you can call Any function like this:
uses Spring, Spring.Collections; var M: IList<Integer>; begin M := TCollections.CreateList<Integer>; M.Add(3); M.Add(1); M.Add(2); M.Add(-1); Writeln( M.Any( function (const AElement: Integer): Boolean begin Result := AElement < 0; end)); // Outputs True Readln; end.
The sort list implementation is the same as the standard IList<T> implementation, but it always adds the values to the list in a sorted order. This is very helpful in some cases when you are going to search elements in the list very often. Because in sorted list the IndexOf and other similar methods are using the binary search which is much faster than doing search in a normal list as in normal list there is just FOR loop over all elements! There is a simple benchmark done just to illustrate the impact of your choice to use the standard list to perform searches! The benchmark is not absolutely correct, but anyway gives main ideas about the difference.
The code of the “benchmark”:
uses Spring, Spring.Collections, Winapi.Windows; var L, SL: IList<Integer>; i: Integer; Tik: Cardinal; begin L := TCollections.CreateList<Integer>; SL := TCollections.CreateSortedList<Integer>; Tik := GetTickCount; for i := 1 to 1000000 do L.Add(i); Writeln('Adding to list: ', GetTickCount - Tik); Tik := GetTickCount; for i := 1 to 1000000 do SL.Add(i); Writeln('Adding to sorted list: ', GetTickCount - Tik); Tik := GetTickCount; for i := 1 to 100000 do L.IndexOf(i); Writeln('Searching list: ', GetTickCount - Tik); Tik := GetTickCount; for i := 1 to 100000 do SL.IndexOf(i); Writeln('Searching sorted list: ', GetTickCount - Tik); end.
The output is following (Intel i7-860). The results are in milliseconds.
Adding to list: 78 Adding to sorted list: 94 Searching list: 17890 Searching sorted list: 31
The multimap is a special interface which did not exist in standard generics but is very useful in some cases. Remember time you created an object dictionary with [doOwnsValues] and the value was TList<T> instance which kept you some matching values for the key? So with the multimap now you can make very easy without manual construction of the value list!
uses Spring, Spring.Collections; var M: IMultiMap<Integer, String>; begin M := TCollections.CreateMultiMap<Integer, String>; M.Add(1, 'One'); M.Add(1, 'First'); M.Add(1, 'Single'); M.Add(2, 'Two'); M.Add(2, 'Second'); Writeln(M.Count); // Outputs 5 Writeln(M.Items.Count); // Outputs 3 end.
The Spring library also implements the dictionary, stack, queue, map. For IDictionary<K, V> you also have method AsReadonlyDictionary<K, V>.