I am delighted to announce the release of Spargine 8 (v2024.8.8.1) on August 1st, 2024. Spargine, my open-source project, now offers NuGet packages for .NET 8. These enhancements have been integrated across all my projects, many of which are currently in production. I encourage you to explore these updates and share any feedback or suggestions for further improvements.
For this release, I’ve concentrated on enhancing performance with the help of GitHub Copilot. This update introduces new methods, benchmarks, and comprehensive unit tests, all aimed at improving Spargine’s performance and reliability, resulting from over 120 commits! Notably, this release includes speed enhancements informed by benchmark tests outlined in the 4th edition of Rock Your Code: Code & App Performance for Microsoft .NET, available on Amazon.
GitHub: https://github.com/RealDotNetDave/dotNetTips.Spargine.8/releases
NuGet: http://bit.ly/dotNetDaveNuGet
You can access all the performance data for these assemblies on GitHub. I continually seek assistance with these projects, especially in writing additional unit tests. If you are interested in contributing, please don’t hesitate to contact me via email at dotnetdave@live.com. Your support and collaboration are highly appreciated!
New Features: Enhanced Type and Collection Utilities
This section highlights some of the new features added to Spargine:
TypeHelper.IsDotNetAssemby
I have added the new method IsDotNetAssembly(), inspired by a post from Gérald Barré. This method allows you to verify if a DLL is a .NET assembly.
Other New Methods
- ArrayExtensions.AsSpan()
- ArrayExtensions.FastHashData(): This method hashes a byte array using SHA256.
- ArrayExtensions.ToFrozenSet()
- ArrayExtenstions.AsReadOnlySpan()
- CollectionExtensions.AsReadOnlySpan()
- CollectionExtensions.AsSpan()
- CollectionExtensions.ToFrozenSet()
- DictionaryExtensions.ToFrozenDictionary()
- DictionaryExtensions.ToImmutableSortedDictionary()
- DictionaryExtensions.ToReadOnlyDictionary()
- EnumerableExtentions.ToFrozenSet()
- EnumerableExtentions.ToImmutableArray()
- ListExtensions.AsReadOnlySpan()
- ListExtensions.ToFrozenSet()
Optimized Utility Methods: Enhancements Across Arrays, Assemblies, DateTime, Lists, Logging, Objects, Strings, and Types
Here are some additional helpful methods in Spargine that I’m sure you and your team will find useful. All have been optimized for performance:
ArrayExtensions
The ArrayExtensions type provides extension methods for arrays, enhancing functionality with additional utility methods.
- AddFirst(): Adds an item to the beginning of the specified array.
- AddLast(): Adds an item to the end of the specified array.
- AreEqual(): Checks if the two arrays are equal.
- BytesToString(): Returns a string that represents this instance.
- ContainsAny(): Determines whether the array contains any of the specified items.
- GenerateHashCode(): Generates a hash code for the entire array.
- RemoveFirst(): Removes the first item in the array.
- RemoveLast(): Removes the last item in the array.
- ToDistinct(): Returns the array without duplicates.
AssemblyExtensions
The AssemblyExtensions type provides extension methods for Assembly to enhance functionality related to type discovery and instantiation.
- GetInstances(): Gets all instances of a specified type within the assembly.
- GetTypes(): Gets the types from the specified assembly that are assignable to the specified type and are not abstract.
DateTimeExtensions
Here are some extension methods from the DateTimeExtensions class that make working with dates and times easier.
GetLastDayOfWeek()
This method calculates and returns the last occurrence of a specified day of the week before or on the given date.
var dateTime = DateTimeOffset.Parse("1/1/2020");
DateTimeOffset result = dateTime.GetLastDayOfWeek(DayOfWeek.Sunday);
Example output: 12/29/2019 12:00:00 AM
GetNextDayOfWeek()
This method returns the next occurrence of a specified day of the week after the given date. Here is an example of how to use it.
DateTimeOffset result = DateTimeOffset.Now.GetNextDayOfWeek(DayOfWeek.Monday);
Intersects()
The Intersects() method determines if a specified date range intersects with the given start date. Here is an example of how to use it.
bool result = now.Intersects(endDate: now.AddDays(100), intersectingStartDate: now.AddDays(1), intersectingEndDate: now.AddDays(10));
IsInRange()
Determines whether a value falls within the specified range defined by a beginning time and an end time. This method is overloaded to work with DateTime, DateTimeOffset, and TimeSpan. Here is an example of how to use it.
var now = DateTime.Now;
bool result = now.IsInRange(beginningTime: new DateTime(1970), endTime: now.AddDays(10));
IsInRangeThrowsException()
Like IsInRange(), this method validates whether a DateTime, TimeSpan, or DateTimeOffset falls within a specified range but throws an exception if it does not. Here is an example of how to use it.
var now = DateTime.Now;
bool result = now.IsInRangeThrowsException(now.Subtract(new TimeSpan(days: 1, hours: 0, minutes: 0, seconds: 0)), now.AddDays(value: 1), paramName: "TEST");
LocalTimeFromUTC()
Given a date, the LocalTimeFromUTC() method converts the UTC time to local time based on a specified timezone. Here is an example of how to use it.
var now = DateTime.Now;
DateTime result = now.LocalTimeFromUtc(timezoneFromUtc: -5);
Max()
The Max() method determines the maximum value between two DateTime or DateTimeOffset values.
Subtract()
The Subtract() method subtracts a TimeSpan value from a DateTime value, resulting in a new DateTime value that represents the original date and time adjusted by the specified TimeSpan.
ToFriendlyDateString()
The ToFriendlyDateString() method formats a DateTime or DateTimeOffset into a human-readable string, such as “Today @ 3:38:55 pm“.
ToMilliEpochTime()
The ToMilliEpochTime() method returns the epoch time value (long) which represents the number of milliseconds that have elapsed since January 1, 1970 (Unix epoch) from which a computer measures system time.
DateTimeFormat
If you find it challenging to remember formatting strings for DateTime, the DateTimeFormat type can be helpful. It supports various formats such as full date/long time, full date/short time, full date and time, general date/long time, general date/short time, long date, month/day, month/year, RFC1123 format, round trip date time, short date, short time, sortable date time, universal full date time, as well as “MMM dd, yyyy” and “MMMM dd, yyyy”.
Here is an example on how to use it:
public static string ToFormattedString(this DateTime input, DateTimeFormat format) => input.ToString(format.DisplayName, CultureInfo.CurrentCulture);
Here are some additional helpful methods related to DateTime manipulation:
- FromMilliEpochTime(): Converts a value representing the time in milliseconds since the Unix epoch (January 1, 1970, 00:00:00 UTC) back into a DateTime object.
- FromUnixTime(): Converts a Unix timestamp to a DateTime object.
- ToFormattedString(): Converts a DateTime object to a formatted string. For example: “Thursday, January 7, 2021 3:36:39 PM”.
If you have any specific examples or further questions about these methods, feel free to ask!
ListExtensions
The ListExtensions type provides extension methods for List to enhance its functionality.
- GenerateHashCode(): Generates a hash code for the entire List based on the hash codes of its elements.
- IndexAtLooped(): Finds index that avoids multiple enumerations.
LoggingHelper
The LoggingHelper type provides utility methods for logging, including functionality to log computer information, application details, and capture all domain exceptions and events.
- RetrieveAllExceptionMessages(): Retrieves all exception messages from the provided exception, including messages from any inner exceptions, as a ReadOnlyCollection.
- RetrieveAllExceptions(): Retrieves all exceptions, including inner exceptions, from the provided exception as a ReadOnlyCollection.
ObjectExtensions
The ObjectExtensions type provides a collection of extension methods for objects, enhancing functionality with additional utility methods for common tasks.
- Clone(): Creates a deep clone of the object.
- ComputeSha256Hash(): Creates a SHA-256 hash of the serialized JSON representation of the object.
- DisposeFields(): Disposes all
IDisposablefields within the object. - FromJson(): Deserializes the JSON string to an object of a type.
- HasProperty(): Determines whether the specified object has a property with the specified name.
- InitializeFields(): Initializes all fields of the specified object that are currently null to their default values. This method uses reflection to iterate through all instance fields of the object and initializes fields that are null and not of a value type. This can be particularly useful for initializing objects that have many fields, reducing the need for manual initialization.
- IsNotNull(): Determines whether the specified object is not null.
- IsNull(): Determines whether the specified object is null.
StringExtensions
The StringExtensions type provides a collection of static methods for string manipulation and checks, enhancing the built-in string functionality.
- EqualsIgnoreCase(): Determines whether the end of this string instance matches the specified string when compared using the specified comparison option.
- FromBase64(): Decodes a string from Base64 encoding.
- Split(): Splits the input string into a ReadOnlyCollection using StringSplitOptions.
- StartsWithOrdinal(): Determines whether the beginning of this string instance matches the specified string when compared using the ordinal sort order.
- SubstringTrim(): Trims whitespace from the start and end of the substring extracted from the specified range of the input string.
- ToBase64(): Converts the input string to its Base64 encoded form.
- ToBrotliStringAsync(): Compresses the specified string using the Brotli algorithm and returns the compressed string in an asynchronous operation.
- ToDeflateStringAsync(): Compresses the specified string using the Deflate algorithm and returns the compressed string in an asynchronous operation.
- ToTrimmed(): Trims the specified string, removing all leading and trailing white-space characters. If the input is null, an empty string is returned.
TypeHelper
Provides a collection of utility methods for type inspection and manipulation, including functionality for creating instances, comparing objects, finding derived types, and working with JSON and assembly data.
- Create(): Creates an instance of the specified type.
- DoesObjectEqualInstance(): Determines whether the specified object equals the specified instance.
- FromJson(): Deserializes a JSON string into an instance of a type.
- FromJsonFile(): Deserializes a JSON file into an instance of a type.
- GetInstanceHashCode(): Computes the hash code for the specified instance.
- GetTypeDisplayName(): Gets the display name of the type of the specified object.
FindDerivedTypes
The FindDerivedTypes() method finds all types derived from a specified base type within the assemblies located in the specified directory. Below is the unit test code for this method:
var result = TypeHelper.FindDerivedTypes(new DirectoryInfo(App.ExecutingFolder()), SearchOption.AllDirectories, typeof(MulticastDelegate), true);
Here is an example of the 360 results from this code:
[0]: {Name = "SyncAsyncEventHandler`1" FullName = "Azure.Core.SyncAsyncEventHandler`1"}
[1]: {Name = "CopyProgressRoutine" FullName = "DotNetTips.Spargine.IO.FileHelper+CopyProgressRoutine"}
[2]: {Name = "VAGetRegisterValue" FullName = "Iced.Intel.VAGetRegisterValue"}
[3]: {Name = "VATryGetRegisterValue" FullName = "Iced.Intel.VATryGetRegisterValue"}
[4]: {Name = "TryConvertToDisp8N" FullName = "Iced.Intel.EncoderInternal.TryConvertToDisp8N"}
[5]: {Name = "InsertColumnFilter`1" FullName = "LinqToDB.InsertColumnFilter`1"}
[6]: {Name = "UpdateColumnFilter`1" FullName = "LinqToDB.UpdateColumnFilter`1"}
[7]: {Name = "InsertOrUpdateColumnFilter`1" FullName = "LinqToDB.InsertOrUpdateColumnFilter`1"}
[8]: {Name = "PostEvictionDelegate`1" FullName = "LinqToDB.Common.Internal.Cache.PostEvictionDelegate`1"}
[9]: {Name = "Clone`1" FullName = "LinqToDB.SqlQuery.ConvertVisitor`1+Clone`1"}
[10]: {Name = "TakeSkipDelegate" FullName = "LinqToDB.Linq.QueryRunner+TakeSkipDelegate"}
Performance Enhancements: Significant Speed Improvements Across Methods
With each Spargine release, significant performance enhancements have been achieved across various methods. Here are some notable improvements:
- ArrayExtentions.PerformAction(): 198 times faster.
- CollectionExtensions.AddRange(): 5.87 times faster.
- EnumerableExtensions.CountAsync(): 4.61 times faster.
- EnumerableExtensions.AddDistinct() (for reference types): 1.144 times faster.
- EnumerableExtensions.FirstOrDefault(): 2.36 times faster.
- EnumerableExtensions.FirstOrDefault() with predicate: 1.22 times faster.
- EnumerableExtensions.FirstOrNull(): 1.7 times faster.
- EnumerableExtensions.FastCount(): 408.42 times faster.
- EnumerableExtensions.HasDuplicates(): 2.21 times faster.
- EnumerableExtensions.HasItems(): 502 times faster!
- EnumerableExtensions.IndexOf(): 2.07 times faster.
- EnumerableExtensions.IndexOf() with comparer: 2.071 times faster.
- EnumerableExtensions.Page(): 1.25 times faster.
- EnumerableExtensions.PickRandom(): 1.09 times faster.
- EnumerableExtensions.RemoveDuplicates(): 2.39 times faster.
- EnumerableExtensions.Shuffle(): 1.35 times faster.
- EnumerableExtensions.Split(): 512 times faster!
- EnumerableExtensions.ToBlockingCollection(): 5.33 times faster.
- EnumerableExtensions.ToDelimitedString(): 176 times faster.
- EnumExtensions.GetDescription(): 3.27 times faster.
- EnumExtensions.GetItems(): 1.05 times faster.
- ExceptionExtensions.GetAllMessages(): 1.03 times faster.
- ExceptionExtensions.GetAllMessagesWithStateTrace(): 2.86 times faster.
- ListExtensions.GenerateHashCode(): 1.02 times faster.
- ListExtensions.HasItems() with predicate: 2.85 times faster.
- ListExtensions.IndexAtLooped(): 1.17 times faster.
- ObjectExtensions.As(): 1.119 times faster.
- ObjectExtensions.Clone() of array: 1.131 times faster.
- ObjectExtensions.Clone() of List: 1.123 times faster.
- ObjectExtensions.Clone() single object: 1.057 times faster.
- ObjectExtensions.ComputeSha256Hash() (for reference types): 1.05 times faster.
- ObjectExtensions.DisposeFields(): 2.84 times faster.
- ObjectExtensions.HasProperty(): 12.59 times faster.
- ObjectExtensions.IsNotNull(): 20.5 times faster.
- ObjectExtensions.InitializeFields(): 1.32 times faster.
- ObjectExtensions.PropertiesToDictionary(): 43 times faster.
- ObjectExtensions.PropertiesToString(): 174 times faster.
- ObjectExtensions.StripNull(): 186 times faster.
- ObjectExtensions.ToJson(): 1.089 times faster.
- RandomData.GenerateAddressRecordCollection(): 1.03 times faster.
- StringExtensions.StartsWithOrdinal(): 1,360 times faster!
- StringExtensions.ToDefateStringAsysnc(): 1.28 times faster.
- StringExtensions.ToTrimmed(): 1.97 times faster.
- StringExtensions.SubstringTrim (): 1.028 times faster.
- TypeHelper.FindDerivedTypes(): 2.08 times faster.
- TypeHelper.GetPropertyValues(): 1.15 times faster.
Summary
I trust these enhanced methods in Spargine will significantly benefit your projects by boosting performance and reliability. Detailed benchmark results are available on GitHub. As always, the success of open-source projects like Spargine relies heavily on community involvement. If you find these updates useful or if you have ideas for further improvements, I encourage you to contribute. Whether it’s by submitting a pull request, reporting issues, or suggesting new features, your input is invaluable.
Together, we can continue to make Spargine a robust and indispensable tool for the .NET community. Your feedback and suggestions are highly appreciated, so please share them in the comments section.
Thank you for your support and keep coding faster and better!
Discover more from dotNetTips.com
Subscribe to get the latest posts sent to your email.
