Monday, 23 September 2024

Looking up gMSA accounts in Active Directory

A short article with tips how you can find gMSA accounts in Active Directory or AD. First off, import the module ActiveDirectory. Then you can run the following snippet to find some gMsa accounts of which you know part of the name of.


Import-Module ActiveDirectory
Get-ADObject -Filter {ObjectClass -eq "msDS-GroupManagedServiceAccount"  -and Name -Like '*SomeGmsa*' }  -Properties DistinguishedName,  SamAccountName | Select DistinguishedName, SamAccountName 


This yields the results:


DistinguishedName                                                   SamAccountName  
-----------------                                                   --------------  
CN=gMSA1DVSomeGmsa,CN=Managed Service Accounts,DC=someacme,DC=org        MSA1DVSomeAcmeP$    
CN=gMSA1_gMSA1DGmsaPT,CN=Managed Service Accounts,DC=someacme,DC=org     MSA1gMSA1DVSomeAcme$
CN=gMSA1_DVSomeGmsaPT,CN=Managed Service Accounts,DC=someacme,DC=org     MSA1DVSomeAcmePT$   

You can search for gMSA users in AD like this:


Import-Module ActiveDirectory 

Get-ADServiceAccount -Filter "Name -like '*SomeGmsa*'"


This should yield a list of matching gMSA users with given name :

You can also ask for all properties of Gmsa users using -Properties with * :



Import-Module ActiveDirectory
Get-ADObject -Filter {ObjectClass -eq "msDS-GroupManagedServiceAccount"  -and Name -Like '*SomeGmsa*' }  -Properties *




Please note that your server should also install the gMsa account where it shall be used ! And to use the module ActiveDirectory, the Windows feature must be installed. From Powershell admin console you can run:


# Remember to install the RSAT-AD-Powershell module 

Add-WindowsFeature RSAT-AD-PowerShell

Install-ADServiceAccount SomeGmsa$


Import-Module ActiveDirectory
Get-ADObject -Filter {ObjectClass -eq "msDS-GroupManagedServiceAccount"  -and Name -Like '*SomeGmsa*' }  -Properties DistinguishedName,  SamAccountName | Select DistinguishedName, SamAccountName 

Note that you gMsa user has a SamAccountName which is suffixed by '$'. This can be set up in IIS for you application as the app pool identity. The username will be in this example:


MYDOMAIN\SomeGmsa$

The password of the gMSa service account will actually be empty ! Instead, the service account is installed as shown above using the cmd-let Install-AdServiceAccount.

Sunday, 8 September 2024

Using lazy loading in Entity Framework Core 8

This article will show some code how you can opt in something called lazy loading in EF. This means you do not load in all the related data for an entity until you need the data. Lets look at a simple entity called Customer. We will add to navigational properties, that is related entities. Without eager loading enabled automatically or lazy loading enabled automatically, EF Core 8 will not populated these navigational properties, which is pointing to the related entities. The fields will be null without active measure on the loading part. Let's inspect how to lazy load such navigational properties.

Customer.cs




 public class Customer {
 
  // more code.. 
 
  public Customer()
  {
      AddressCustomers = new HashSet<AddressCustomer>();
  }
  
  // more code .. 
 
  private Customer(ILazyLoader lazyLoader)
  {
    LazyLoader = lazyLoader;
  }

  public CustomerRank CustomerRank { get; set; }

  public virtual ICollection<AddressCustomer> AddressCustomers { get; set; }
  
 }
  
  
 
 
 
First off, the ILazyLoader service is from Microsoft.EntityFrameworkCore.Infrastructure. It is injected inside the entity, preferably using a private constructor of the entity. Now you can set up lazy loading a for a navigational property like this :



 public CustomerRank CustomerRank
 {
     get => LazyLoader.Load(this, ref _customerRank);
     set => _customerRank = value;
 }
  
  
 
 
 
If it feels a bit unclean to mix entity code with behavioral code since we inject a service into our domain models or entities, you can use the Fluent api instead while setting up the DbContext.



  modelBuilder.Entity<Customer>()
      .Navigation(e => e.AddressCustomers)
     .AutoInclude();

  modelBuilder.Entity<Customer>(entity =>
  {
      entity.HasKey(e => e.Id);
     entity.Navigation(e => e.CustomerRank).AutoInclude();
  });


 
 
 
If automatically lazy loading the data (the data will be loaded upon access of the navigational property) seems a bit little flexible, one can also set up loading manually wherever in the application code using the methods Entry and either Reference or Collection and then the Load method.



var customer = _dbContext.Customers.First();

_dbContext
    .Entry(customer)
    .Reference(c => c.CustomerRank)
    .Load();


_dbContext
    .Entry(customer)
    .Collection(c => c.AddressCustomers)
    .Load();


Once more, note that the data is still lazy loaded, their content will only be loaded when you access the particular navigational property pointing to the related data. Also note that if you debug in say VS 2022, data might look like they are automatically loaded, but this is because the debugger loads the contents if it can and will even do so for lazy loaded navigational fields. If you instead make in your application code a programmatic access to this navigational property and output the data you will see the data also being loaded, but this happens once it is programatic access. For example if we made the private field _customerRank public (as we should not do to protect our domain model's data) you can see this while debugging :


//changed a field in Customer.cs to become public for external access :
//  public CustomerRank _customerRank;

Console.WriteLine(customer._customerRank);
Console.WriteLine(customer.CustomerRank);

// considering this set up 

  public CustomerRank CustomerRank
  {
      get => LazyLoader.Load(this, ref _customerRank);
      set => _customerRank = value;
  }



The field _customerRank is initially null, it is when we access the property CustomerRank which I set to be AutoInclude i.e. lazy loaded I see that data is loaded.

Saturday, 7 September 2024

Using implicit index access in object initializers using the hat operator in C# 13

This article presents a small sample how to use implicit index access in object initializers in C# 13, using the 'hat' operator. This is a very specialized functionality, but it can become handing when you intialize objects and their collection fields and properties. For example, creating an object with an array and just doing some few adjustments on the collection becomes a bit more accessible. Let's see some code what this new feature in C# 13 is :


var nums = new[] { 6, 9, 4, 20 };

var lastElement = nums[^1]; //last element via index 

Console.WriteLine(lastElement);

var greeter = new Greeter { 
    Message = { 
        [^1] = '!'
    },
     SomeNums = 
     {
         [^1] = 5,
         [^2] = 4,
         [^3] = 3,
         [^4] = 2,
         [^5] = 1
     }
};

Console.WriteLine(greeter);

Console.ReadKey();

class Greeter
{
    public char[] Message { get; set; } = "Hello?".ToCharArray();

    public int[] SomeNums = new int[5];

    public override string ToString()
    {
        return $"Message = {string.Join(',', Message)}, SomeNums = {string.Join(',', SomeNums)}";
    }
}


This outputs the following :


20
Message = H,e,l,l,o,!, SomeNums = 1,2,3,4,5


We initialize an instance of the class Greeter into the variable greeter and set the fields / properties in the initializer. We already inited the property Message to "Hello?" and at the same type, in the object initalizer, alter the last letter where we use the hat operator using : [^1] = '!' , which results into the string "Hello!". Here we initalized an object in an easy way and at the same time was able to alter the contents of collections of the object, altering in the example the last char of a char array. We also set the int array one element at a time using again the hat operator here. We could of course not use the hat operator here, which will access the nth last element in a collection. Make note that the hat operator is 1-based, so the last element is ^1, the second last element is [^2]. Note that to use C#, you have to download the .NET 9 SDK preview from this url : https://dotnet.microsoft.com/en-us/download/dotnet/9.0 Make note that you select the .NET 9 SDK for the platform you are using. Most .NET developers on Windows can download x64 SDK for example. After installing the SDK , go to Tools => Options in VS 2022 and choose Environment : Preview features. Check the option : 'Use previews of .NET SDK'. Restart VS 2022 after doing this. You can now test out C# 13 and .NET 9 and for example create a console application for .NET projects (not .NET Framework). The .csproj in my sample code looks like this, make note of the TargetFramework, set it to net9.0. Also set the LangVersion to Preview.



<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
	 <LangVersion>Preview</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>



Saturday, 10 August 2024

Calculating DateTime in .NET from Unix timestamp

This article will describe how to calculate datetime in .NET . A Unix timestamp is defined as : - The number of SECONDS that has passed SINCE Unix Epoch, a defined date set to January 1st, 1970 at UTC (Universal Time Coordinated). We can calculate a Unix timestamp for now time if we call the following method:

	DateTimeOffset.UtcNow.ToUnixTimeSeconds()

For example, as of 10th August 2024 at 21:41 this method gives: 1723318757 To calculate the DateTime value from a Unix Timestamp we can define the following extension method, since we will have an interesting issue in 2038 on 19th January 2038 at 03:14:08 UTC. At that time, an overflow will occur for systems using 32-bits integers, which is actually quite a lot of systems included 32-bits time stamps in database systems. A solution of this is to switch to 64-bits long, but this is as stated not an issue until 2038, which is still about 14 years into the future as of 2024.. We will create extension methods for both seconds and milliseconds because sometimes Unix timestamps are given in milliseconds, and we should also support both int and long. In the future, that is 2038, int cannot be used, long must be used. In addition, extension method on string is also added for convenience.


public static class DateTimeExtensions {

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}


Here is how the method could be used in some examples:


    DateTime? dt = "1723318757".FromUnixTimeSeconds();
    DateTime? dt2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString().FromUnixTimeSeconds();
	
	Console.WriteLine(dt.Value);
	Console.WriteLine(dt.Value.Kind);
	Console.WriteLine(dt2);
    

The screenshot below shows how it can be calculated.








Let's next look at an extension method that converts a DateTime to Unix time stamp.


    public static long ToUnixTimeSeconds(this DateTime dt) =>
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;



Usage example:

DateTime? dt4 = DateTime.UtcNow;
Console.WriteLine(dt4.ToUnixTimeSeconds());    

This gives the unix time stamp from current time. Please note that it also possible to cast a DateTime into a DateTimeOffset and then just use built in method ToUnitTimeSeconds


long unixTimestamp5 = ((DateTimeOffset)(dt4)).ToUnixTimeSeconds();
Console.WriteLine(unixTimestamp5);
    

In addition to the methods above, adding support for milliseconds should be fairly straightforward. Officially, Unix time stamps are measured in seconds. And when you convert from a Unix timestamp to a datetime, you will get UTC time. To get the local time you can do this:

DateTime? dt = "1723325214".FromUnixTimeSeconds();
Console.WriteLine(dt.Value.ToLocalTime()); 
    

Use the method ToLocalTime() on the datetime of Kind Utc, i.e. convert from UTC datetime to local datetime, taking your time zone into account. The entire extension method class then looks like this:

DateTimeExtensions.cs



public static class DateTimeExtensions {

	public static long ToUnixTimeSeconds(this DateTime dt) => 
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}



Friday, 9 August 2024

Converting an UTC datetime into a datetime in an arbitrary time zone

This article will show a method how to convert an UTC datetime into an arbitrary time zone. Here is the extension method for that shown below. The method checks that the specified datetime object has Kind set to Utc, we can't correctly recalculate non UTC date times that might be Local or Unspecified. We can specify the Kind of a DateTime when we specify it for example :

    var someUtcDate = new DateTime(2024, 6, 4, 6, 30, 0, DateTimeKind.Utc); 

We can also use the DateTime.SpecifyKind method:


  var someUtcDate = DateTime.SpecifyKind(someotherDate, DateTimeKind.Utc); 

Please note however, loading datetimes that are not Utc will quickly run into issues when trying to transform a not specified Utc date into another time zone.

DateTimeExtensions.cs


public static class DatetimeExtensions {
    
    /// Returns the DateTime in a specified time zone
    /// 
    ///  must be a recognized time zone id, check with System.TimeZoneInfo.GetSystemTimeZones() available time zones on the target system
    ///  must be a datetime of Kind Utc to be properly calculated into its proper value in the specified time zone id
    /// 
    public static DateTime ConvertToLocalTime(this DateTime dateTime, string timeZoneId){
    
        TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId) ?? System.TimeZoneInfo.GetSystemTimeZones().FirstOrDefault(tz => string.Equals(tz.Id, timeZoneId, StringComparison.InvariantCultureIgnoreCase));
        if (timeZone == null)
        {
            throw new ArgumentException($"The time zone with id {timeZoneId} is not recognized. (Available time zones can be retrieved using 'System.TimeZoneInfo.GetSystemTimeZones()' method)");
        }
        if (dateTime.Kind != DateTimeKind.Utc)
        {
            throw new ArgumentException($"The date time {dateTime} is not of the right Kind 'Utc'");
        }
        return TimeZoneInfo.ConvertTimeFromUtc(dateTime, timeZone);     
    }
    
}


And here is some sample code to test out this method:

Program.cs


void Main()
{
    
    var central = TimeZoneInfo.GetSystemTimeZones().Where(x => x.DisplayName.Contains("Central")).ToList(); 
    central.Dump();
    
    var someUtcDate = new DateTime(2024, 6, 4, 6, 30, 0, DateTimeKind.Utc); 
    var someUtcDateInCST = someUtcDate.ConvertToLocalTime("Central Standard Time"); 
    Console.WriteLine(someUtcDateInCST);
}


This outputs the answer:
04.06.2024 01:30:00

Note that 'Central Standard Time', which is in the Midtwest of USA, is 6 hours behind UTC. But we only are 5 hours behind UTC in the summer half year, since we got daylight saving. Please note, the example time zone used has daylight saving hours, you should use time zones that supports daylight saving to get correct results, in Norway for example we are either two hours or one hour ahead of UTC. Two hours when it is 'summer time' or 'daylight saving hour'. The following image shows the first time zones called something called 'Central' :

Here are the time zones available in Windows 11 systems per default ( I have no custom time zones added ) :

TimeZoneInfo - 141 different time zones in Windows 11 systems (example setup)
(141 items)
IdDisplayName
Dateline Standard Time(UTC-12:00) International Date Line West
UTC-11(UTC-11:00) Coordinated Universal Time-11
Aleutian Standard Time(UTC-10:00) Aleutian Islands
Hawaiian Standard Time(UTC-10:00) Hawaii
Marquesas Standard Time(UTC-09:30) Marquesas Islands
Alaskan Standard Time(UTC-09:00) Alaska
UTC-09(UTC-09:00) Coordinated Universal Time-09
Pacific Standard Time (Mexico)(UTC-08:00) Baja California
UTC-08(UTC-08:00) Coordinated Universal Time-08
Pacific Standard Time(UTC-08:00) Pacific Time (US & Canada)
US Mountain Standard Time(UTC-07:00) Arizona
Mountain Standard Time (Mexico)(UTC-07:00) La Paz, Mazatlan
Mountain Standard Time(UTC-07:00) Mountain Time (US & Canada)
Yukon Standard Time(UTC-07:00) Yukon
Central America Standard Time(UTC-06:00) Central America
Central Standard Time(UTC-06:00) Central Time (US & Canada)
Easter Island Standard Time(UTC-06:00) Easter Island
Central Standard Time (Mexico)(UTC-06:00) Guadalajara, Mexico City, Monterrey
Canada Central Standard Time(UTC-06:00) Saskatchewan
SA Pacific Standard Time(UTC-05:00) Bogota, Lima, Quito, Rio Branco
Eastern Standard Time (Mexico)(UTC-05:00) Chetumal
Eastern Standard Time(UTC-05:00) Eastern Time (US & Canada)
Haiti Standard Time(UTC-05:00) Haiti
Cuba Standard Time(UTC-05:00) Havana
US Eastern Standard Time(UTC-05:00) Indiana (East)
Turks And Caicos Standard Time(UTC-05:00) Turks and Caicos
Paraguay Standard Time(UTC-04:00) Asuncion
Atlantic Standard Time(UTC-04:00) Atlantic Time (Canada)
Venezuela Standard Time(UTC-04:00) Caracas
Central Brazilian Standard Time(UTC-04:00) Cuiaba
SA Western Standard Time(UTC-04:00) Georgetown, La Paz, Manaus, San Juan
Pacific SA Standard Time(UTC-04:00) Santiago
Newfoundland Standard Time(UTC-03:30) Newfoundland
Tocantins Standard Time(UTC-03:00) Araguaina
E. South America Standard Time(UTC-03:00) Brasilia
SA Eastern Standard Time(UTC-03:00) Cayenne, Fortaleza
Argentina Standard Time(UTC-03:00) City of Buenos Aires
Montevideo Standard Time(UTC-03:00) Montevideo
Magallanes Standard Time(UTC-03:00) Punta Arenas
Saint Pierre Standard Time(UTC-03:00) Saint Pierre and Miquelon
Bahia Standard Time(UTC-03:00) Salvador
UTC-02(UTC-02:00) Coordinated Universal Time-02
Greenland Standard Time(UTC-02:00) Greenland
Mid-Atlantic Standard Time(UTC-02:00) Mid-Atlantic - Old
Azores Standard Time(UTC-01:00) Azores
Cape Verde Standard Time(UTC-01:00) Cabo Verde Is.
UTC(UTC) Coordinated Universal Time
GMT Standard Time(UTC+00:00) Dublin, Edinburgh, Lisbon, London
Greenwich Standard Time(UTC+00:00) Monrovia, Reykjavik
Sao Tome Standard Time(UTC+00:00) Sao Tome
Morocco Standard Time(UTC+01:00) Casablanca
W. Europe Standard Time(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
Central Europe Standard Time(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
Romance Standard Time(UTC+01:00) Brussels, Copenhagen, Madrid, Paris
Central European Standard Time(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb
W. Central Africa Standard Time(UTC+01:00) West Central Africa
GTB Standard Time(UTC+02:00) Athens, Bucharest
Middle East Standard Time(UTC+02:00) Beirut
Egypt Standard Time(UTC+02:00) Cairo
E. Europe Standard Time(UTC+02:00) Chisinau
West Bank Standard Time(UTC+02:00) Gaza, Hebron
South Africa Standard Time(UTC+02:00) Harare, Pretoria
FLE Standard Time(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius
Israel Standard Time(UTC+02:00) Jerusalem
South Sudan Standard Time(UTC+02:00) Juba
Kaliningrad Standard Time(UTC+02:00) Kaliningrad
Sudan Standard Time(UTC+02:00) Khartoum
Libya Standard Time(UTC+02:00) Tripoli
Namibia Standard Time(UTC+02:00) Windhoek
Jordan Standard Time(UTC+03:00) Amman
Arabic Standard Time(UTC+03:00) Baghdad
Syria Standard Time(UTC+03:00) Damascus
Turkey Standard Time(UTC+03:00) Istanbul
Arab Standard Time(UTC+03:00) Kuwait, Riyadh
Belarus Standard Time(UTC+03:00) Minsk
Russian Standard Time(UTC+03:00) Moscow, St. Petersburg
E. Africa Standard Time(UTC+03:00) Nairobi
Volgograd Standard Time(UTC+03:00) Volgograd
Iran Standard Time(UTC+03:30) Tehran
Arabian Standard Time(UTC+04:00) Abu Dhabi, Muscat
Astrakhan Standard Time(UTC+04:00) Astrakhan, Ulyanovsk
Azerbaijan Standard Time(UTC+04:00) Baku
Russia Time Zone 3(UTC+04:00) Izhevsk, Samara
Mauritius Standard Time(UTC+04:00) Port Louis
Saratov Standard Time(UTC+04:00) Saratov
Georgian Standard Time(UTC+04:00) Tbilisi
Caucasus Standard Time(UTC+04:00) Yerevan
Afghanistan Standard Time(UTC+04:30) Kabul
West Asia Standard Time(UTC+05:00) Ashgabat, Tashkent
Qyzylorda Standard Time(UTC+05:00) Astana
Ekaterinburg Standard Time(UTC+05:00) Ekaterinburg
Pakistan Standard Time(UTC+05:00) Islamabad, Karachi
India Standard Time(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi
Sri Lanka Standard Time(UTC+05:30) Sri Jayawardenepura
Nepal Standard Time(UTC+05:45) Kathmandu
Central Asia Standard Time(UTC+06:00) Bishkek
Bangladesh Standard Time(UTC+06:00) Dhaka
Omsk Standard Time(UTC+06:00) Omsk
Myanmar Standard Time(UTC+06:30) Yangon (Rangoon)
SE Asia Standard Time(UTC+07:00) Bangkok, Hanoi, Jakarta
Altai Standard Time(UTC+07:00) Barnaul, Gorno-Altaysk
W. Mongolia Standard Time(UTC+07:00) Hovd
North Asia Standard Time(UTC+07:00) Krasnoyarsk
N. Central Asia Standard Time(UTC+07:00) Novosibirsk
Tomsk Standard Time(UTC+07:00) Tomsk
China Standard Time(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi
North Asia East Standard Time(UTC+08:00) Irkutsk
Singapore Standard Time(UTC+08:00) Kuala Lumpur, Singapore
W. Australia Standard Time(UTC+08:00) Perth
Taipei Standard Time(UTC+08:00) Taipei
Ulaanbaatar Standard Time(UTC+08:00) Ulaanbaatar
Aus Central W. Standard Time(UTC+08:45) Eucla
Transbaikal Standard Time(UTC+09:00) Chita
Tokyo Standard Time(UTC+09:00) Osaka, Sapporo, Tokyo
North Korea Standard Time(UTC+09:00) Pyongyang
Korea Standard Time(UTC+09:00) Seoul
Yakutsk Standard Time(UTC+09:00) Yakutsk
Cen. Australia Standard Time(UTC+09:30) Adelaide
AUS Central Standard Time(UTC+09:30) Darwin
E. Australia Standard Time(UTC+10:00) Brisbane
AUS Eastern Standard Time(UTC+10:00) Canberra, Melbourne, Sydney
West Pacific Standard Time(UTC+10:00) Guam, Port Moresby
Tasmania Standard Time(UTC+10:00) Hobart
Vladivostok Standard Time(UTC+10:00) Vladivostok
Lord Howe Standard Time(UTC+10:30) Lord Howe Island
Bougainville Standard Time(UTC+11:00) Bougainville Island
Russia Time Zone 10(UTC+11:00) Chokurdakh
Magadan Standard Time(UTC+11:00) Magadan
Norfolk Standard Time(UTC+11:00) Norfolk Island
Sakhalin Standard Time(UTC+11:00) Sakhalin
Central Pacific Standard Time(UTC+11:00) Solomon Is., New Caledonia
Russia Time Zone 11(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky
New Zealand Standard Time(UTC+12:00) Auckland, Wellington
UTC+12(UTC+12:00) Coordinated Universal Time+12
Fiji Standard Time(UTC+12:00) Fiji
Kamchatka Standard Time(UTC+12:00) Petropavlovsk-Kamchatsky - Old
Chatham Islands Standard Time(UTC+12:45) Chatham Islands
UTC+13(UTC+13:00) Coordinated Universal Time+13
Tonga Standard Time(UTC+13:00) Nuku'alofa
Samoa Standard Time(UTC+13:00) Samoa
Line Islands Standard Time(UTC+14:00) Kiritimati Island

Tuesday, 23 July 2024

Displaying Emojis in Windows based consoles

This article will show you how you can get started with showing emojis in Windows based consoles. For an initial peek - Here is a video showing the available Emojis on Windows system I found using Nerd Font Caskaydia Cove mono font glyphs :


First off, some code showing how we can output Emojis ! We will list emojis and their code point value, a hexadecimal integer value that is used to get the correct char that represents an Emoji. Here is the console app.
Program.cs


using System.Text;

Console.OutputEncoding = System.Text.Encoding.UTF8;

var sb = new StringBuilder();
int emojisAdded = 0;
const int EMOJIS_PER_LINE = 10;
int codePoint = 0x1F300;

// Define the ranges of emojis
int[][] emojiRanges = new int[][]
{
            new int[] { 0x1F300, 0x1F5FF },
            new int[] { 0x1F600, 0x1F64F },
            new int[] { 0x1F680, 0x1F6FF },
            new int[] { 0x1F900, 0x1F9FF },
            new int[] { 0x1FA70, 0x1FAFF }
};

foreach (var emojiRange in emojiRanges)
{

    for (codePoint = emojiRange[0]; codePoint <= emojiRange[1]; codePoint++)
    {
        string emoji = char.ConvertFromUtf32(codePoint);
        sb.Append(emoji);
        emojisAdded++;
        if (emojisAdded % EMOJIS_PER_LINE == 0)
        {
            Console.WriteLine($"{sb.ToString()} {codePoint - 9:X}- {codePoint:X}");
            sb.Clear();
        }
    }

    Console.WriteLine(sb.ToString() + " " + (codePoint - 9).ToString("X") + (codePoint.ToString("X"))); //print remaining emojis

}


We use some known ranges of code points, hexadecimal valued integers where we know we in UTF-8 will find Emoji. We set up UTF-8 as the Console output encoding. We will use the method char.ConvertFromUtf32 here and the font will render the Emoji as a Glyph. Please note that not all consoles support Emojis in Windows ! We use Windows Terminal and I have set up a font that supports emojis, 'CaskaydiaCove Nerd Font Mono'. You can find the font and .ttf files to install on Github, just search for "Nerd font" and you will find it here :

https://github.com/ryanoasis/nerd-fonts/releases

If we compile the program and open it in Windows Terminal, you should get something similar to what shown below. I see there are issues on the very last emojis as they are not outputted correct, in the group "Symbols and Pictographs Extended-A". Emoticons: U+1F600 to U+1F64F Miscellaneous Symbols and Pictographs: U+1F300 to U+1F5FF Transport and Map Symbols: U+1F680 to U+1F6FF Supplemental Symbols and Pictographs: U+1F900 to U+1F9FF Symbols and Pictographs Extended-A: U+1FA70 to U+1FAFF Here are screenshots from the Emoji demo, ten Emojis are shown per row ! The "code point" integer range for each row is displayed to the right.

Monday, 15 July 2024

Caching pure functions using Memoize in C#

This article will present a technique for caching pure functions in C# using Memoize technique. This is a programmatic caching of pure method or function where we have a method that always returns the same result or equivalent result given an input. This adds scalability, maybe the method takes long time to process and we want to avoid using resources and provide a quicker answer. If your method has side effects or does not yield the same or equivalent result (cosmetic changes ignored) given a set of parameter(s), it should not be memoized. But if it does, here is how you can do this. Note that memoize is a general technique used in functional programming and is used in many languages such as Javascript, for example in the Underscore.Js lib. First off, let's define some POCOs to test the memoize function out. We will use a small sample set of movies and their actors and additional information from the fabulous year 1997.

MovieStore.cs


public class MovieStore {
    public string GetActorsByMovieTitle(string movieTitle)
    {
        Console.WriteLine($"Retrieving actors for movie with title {movieTitle} at: {DateTime.Now}");
        List<Movie> movies1997 = System.Text.Json.JsonSerializer.Deserialize<List<Movie>>(movies1997json);
        string actors = string.Join(",", movies1997
        	.FirstOrDefault(m => m.name?.ToLower() == movieTitle?.ToLower())?.actors.ToArray());
        return actors;
    }   
    
    string movies1997json = """
[
{
  "name": "The Lost World: Jurassic Park",
  "year": 1997,
  "runtime": 129,
  "categories": [
    "adventure",
    "action",
    "sci-fi"
  ],
  "releasedate": "1997-05-23",
  "director": "Steven Spielberg",
  "writer": [
    "Michael Crichton",
    "David Koepp"
  ],
  "actors": [
    "Jeff Goldblum",
    "Julianne Moore",
    "Pete Postlethwaite"
  ],
  "storyline": "Four years after the failure of Jurassic Park on Isla Nublar, John Hammond reveals to Ian Malcolm that there was another island (\"Site B\") on which dinosaurs were bred before being transported to Isla Nublar. Left alone since the disaster, the dinosaurs have flourished, and Hammond is anxious that the world see them in their \"natural\" environment before they are exploited."
},
{
  "name": "The Fifth Element",
  "year": 1997,
  "runtime": 127,
  "categories": [
    "action",
    "adventure",
    "sci-fi"
  ],
  "releasedate": "1997-05-09",
  "director": "Luc Besson",
  "writer": [
    "Luc Besson",
    "Robert Mark Kamen"
  ],
  "actors": [
    "Bruce Willis",
    "Milla Jovovich",
    "Gary Oldman",
    "Chris Tucker",
    "Ian Holm",
    "Luke Perry",
    "Brion James",
    "Tommy Lister",
    "Lee Evans",
    "Charlie Creed-Miles",
    "John Neville",
    "John Bluthal",
    "Mathieu Kassovitz",
    "Christpher Fairbank"
  ],
  "storyline": "In the colorful future, a cab driver unwittingly becomes the central figure in the search for a legendary cosmic weapon to keep Evil and Mr. Zorg at bay."
} ,
{
  "name": "Starship Troopers",
  "year": 1997,
  "runtime": 129,
  "categories": [
    "action",
    "adventure",
    "sci-fi",
    "thriller"
  ],
  "releasedate": "1997-11-07",
  "director": "Paul Verhoeven",
  "writer": [
    "Edward Neumeier",
    "Robert A. Heinlein"
  ],
  "actors": [
    "Casper Van Dien",
    "Dina Meyer",
    "Denise Richards",
    "Jake Busey",
    "Neil Patrick Harris",
    "Clancy Brown",
    "Seth Gilliam",
    "Patrick Muldoon",
    "Michael Ironside"
  ],
  "storyline": "In the distant future, the Earth is at war with a race of giant alien insects. Little is known about the Bugs except that they are intent on the eradication of all human life. But there was a time before the war... A Mobile Infantry travels to distant alien planets to take the war to the Bugs. They are a ruthless enemy with only one mission: Survival of their species no matter what the cost..."
}
]
""";
}




Movie.cs


public class Movie
{
    public string name { get; set; }
    public int year { get; set; }
    public int runtime { get; set; }
    public List<string> categories { get; set; }
    public string releasedate { get; set; }
    public string director { get; set; }
    public List<string> writer { get; set; }
    public List<string> actors { get; set; }
    public string storyline { get; set; }
}


Let's suppose the method GetActorsByMovieTitle is called many times or takes a lot of time to calculate. We want to cache it, to memoize it. It will be cached in a simple manner using memoize. This will short term cache the results, if we would like to persist the memoized results for long duration, we would use some other caching service such as database or Redis cache. The caching will function in sequential calls inside the same scope, it could be scoped as a singleton and long term cached inside memory for example. So here is how we can do the memoization shown below.

FunctionalExtensions.cs


public static Func<T1, TOut> Memoize<T1, TOut>(this Func<T1, TOut> @this, Func<T1, string> keyGenerator)
	{
		var dict = new Dictionary<string, TOut>();
		return x =>
		{
			string key = keyGenerator(x);
			if (!dict.ContainsKey(key))
			{
				dict.Add(key, @this(x));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, TOut> Memoize<T1, T2, TOut>(this Func<T1, T2, TOut> @this, Func<T1, T2, string> keyGenerator)
	{
		var dict = new Dictionary<string, TOut>();
		return (x,y) =>
		{
			string key = keyGenerator(x,y);
			if (!dict.ContainsKey(key))
			{
				dict.Add(key, @this(x,y));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, T3, TOut> Memoize<T1, T2, T3, TOut>(this Func<T1, T2, T3, TOut> @this, Func<T1, T2, T3, string> keyGenerator)
	{
		var dict = new Dictionary<string, TOut>();
		return (x, y, z) =>
		{
			string key = keyGenerator(x, y,z);
			if (!dict.ContainsKey(key))
			{
				dict.Add(key, @this(x, y, z));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, T3, T4, TOut> Memoize<T1, T2, T3, T4, TOut>(this Func<T1, T2, T3, T4, TOut> @this, Func<T1, T2, T3, T4, string> keyGenerator)
	{
		var dict = new Dictionary<string, TOut>();
		return (x, y, z, w) =>
		{
			string key = keyGenerator(x, y, z, w);
			if (!dict.ContainsKey(key))
			{
				dict.Add(key, @this(x, y, z, w));
			}
			return dict[key];
		};
	}


As we see above, we use a dictionary inside the memoize overloads and the way generics works, a dictionary will live inside each overloaded method accepting a different count of generic type parameters. We also provide a keyGenerator method that must be supplied to specify how we build up a unique key that we decide how we shall key each results from the given set of parameter(s). Note that we return here a function result, that is a func, that returns TOut and accepts the specified parameters in each overload. T1 or T1,T2 or T1,T2,T3 or T1,T2,T3,T4 and so on. Expanding the methods above to for example 16 parameters would be fairly easy, the code above shows how we can add support for more and more parameters. I believe you should avoid methods with more than 7 parameters,
but the code above should be clear. We return a func and we also accept also a func which returns TOut and same amount of parameters of same types T1,.. in each overload. Okay, next up an example how we can use this memoize function in the main method.

Program.cs


void Main()
{
    var movieStore = new MovieStore();
    
    //string actors = movieStore.GetActorsByMovieTitle("Starship troopers");
    //actors.Dump("Starship Troopers - Actors");
    //
    //Demo of memoized function
    
    var GetActorsByMovieTitle = ((string movieTitle) => movieStore.GetActorsByMovieTitle(movieTitle));
    var GetActorsByMovieTitleM = GetActorsByMovieTitle.Memoize(x => x);
    
    var starShipTroopersActors1 = GetActorsByMovieTitleM("Starship troopers");
    starShipTroopersActors1.Dump("Starship troopers - Call to method #1 time");
    var starShipTroopersActors2 = GetActorsByMovieTitleM("Starship troopers");
    starShipTroopersActors2.Dump("Starship troopers - Call to method #2 time");
    var starShipTroopersActors3 = GetActorsByMovieTitleM("Starship troopers");
    starShipTroopersActors3.Dump("Starship troopers - Call to method #3 time");
}


Note that in the test case above we send in one parameter T1 of type string, which is a movie title and we declare a func variable first using a lambda. We have to do the memoization in two declarations here and we use the convention that we suffix the memoized function with 'M' for 'Memoize'

Program.cs


void Main()
{
    var movieStore = new MovieStore();    
    var GetActorsByMovieTitle = ((string movieTitle) => movieStore.GetActorsByMovieTitle(movieTitle));
    var GetActorsByMovieTitleM = GetActorsByMovieTitle.Memoize(x => x);

The code has added a Console.WriteLine in the method which is memoized to check how many times the method is actually called or the cached result is returned instead. A run in Linqpad 7 is shown in screenshot below, showing that the output is cached correct. Note that if we wanted a thread implementation, we could instead use ConcurrentDictionary for example. The following methods show how we can do this. We exchanged Dictionary with ConcurrentDictionary and exchanged Add with TryAdd method of ConcurrentDictionary.

Program.cs


	public static Func<T1, TOut> MemoizeV2<T1, TOut>(this Func<T1, TOut> @this, Func<T1, string> keyGenerator)
	{
		var dict = new ConcurrentDictionary<string, TOut>();
		return x =>
		{
			string key = keyGenerator(x);
			if (!dict.ContainsKey(key))
			{
				dict.TryAdd(key, @this(x));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, TOut> MemoizeV2<T1, T2, TOut>(this Func<T1, T2, TOut> @this, Func<T1, T2, string> keyGenerator)
	{
		var dict = new ConcurrentDictionary<string, TOut>();
		return (x, y) =>
		{
			string key = keyGenerator(x, y);
			if (!dict.ContainsKey(key))
			{
				dict.TryAdd(key, @this(x, y));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, T3, TOut> MemoizeV2<T1, T2, T3, TOut>(this Func<T1, T2, T3, TOut> @this, Func<T1, T2, T3, string> keyGenerator)
	{
		var dict = new ConcurrentDictionary<string, TOut>();
		return (x, y, z) =>
		{
			string key = keyGenerator(x, y, z);
			if (!dict.ContainsKey(key))
			{
				dict.TryAdd(key, @this(x, y, z));
			}
			return dict[key];
		};
	}
	public static Func<T1, T2, T3, T4, TOut> MemoizeV2<T1, T2, T3, T4, TOut>(this Func<T1, T2, T3, T4, TOut> @this, Func<T1, T2, T3, T4, string> keyGenerator)
	{
		var dict = new ConcurrentDictionary<string, TOut>();
		return (x, y, z, w) =>
		{
			string key = keyGenerator(x, y, z, w);
			if (!dict.ContainsKey(key))
			{
				dict.TryAdd(key, @this(x, y, z, w));
			}
			return dict[key];
		};
	}


Hopefully, memoize or the process of memoization should be clearer now. It is a call based caching technique used preferably for pure functions / methods that has the same or equivalent result given a set of input parameter(s) and we memoize the function / method and cache the results. When used inside e.g. a singleton, we can cache longer time in memory and achieve performance boosts. You could do the same of course using a static variable, but the memoize technique is more generic purpose and is a pattern that is used in many programming languages. F# usually got way better support for functional programming than C#, but actually lacks a built in memoization functionality. Other languages do support memoization built in, such as in Python and LISP. The following screen shot shows a run of memoization above, I used ConcurrentDictionary when I tested.

Tuesday, 2 July 2024

Maybe Monad in C# - Guarding against nulls

This article will look more at the Maybe monad in C#. It is used to guard against nulls and is one of the most known monads in functional programming. The Maybe monad is also called Option or Optional many places. The screenshot below shows the allowed state transitions.

First off, records will be used since they support immutability out of the box.

Maybe.cs


public abstract record Maybe<T>();
public record Nothing<T> : Maybe<T>;
public record UnhandledNothing<T> : Nothing<T>;
public record Something<T>(T Value) : Maybe<T>;
public record Error<T>(Exception CapturedError) : Maybe<T>;
public record UnhandledError<T>(Exception CapturedError) : Error<T>(CapturedError);


As we see, the base record Maybe of T is abstract and can be one of several subtypes. Nothing means there are no value contained inside the 'container' or monad. Something of T means a value is inside the 'container'. This value can be null, but the container wrapping this value is not null, hence by sticking to monads such as Maybe we avoid null issues for the code that lives outside using the container, accessing the container. Maybe container here is no magic bullet, but it will make it easier to avoid null issues in your code in the parts where it is used. Let's next look at extension methods for the Maybe types defined in the records above.

MaybeExtensions.cs


public static class MaybeExtensions
{

	/// <summary>ToMayBe operates as a Return function in functional programming, lifting a normal value into a monadic value</summary>
	public static Maybe<T> ToMaybe<T>(this T @this)
	{		
		if (!EqualityComparer<T>.Default.Equals(@this, default))
		{
			return new Something<T>(@this);
		}
		else if (@this != null && Nullable.GetUnderlyingType(@this.GetType()) == null && @this.GetType().IsPrimitive){
			//primitive types that are not nullable and has got a default value are to be considered to have contents and have Something<T>, for example int value 0 or bool value false
			return new Something<T>(@this);
		}		
		return new Nothing<T>();
	}
	
	/// <summary>TryGetValue is similar to Reduce method in functional programming, but signal a boolean flag if the retrieval of value was successful</summary>
	public static bool TryGetValue<T>(this Maybe<T> @this, out T value){
	 	value = @this switch {
			Something<T> s => s.Value,
			_ => default(T)			
		};
		return @this is Something<T>;
	}
	
    //<summary>Call method Bind first to get correct behavior;/summary>
	public static Maybe<T> OnSomething<T>(this Maybe<T> @this, Action<T> actionOnSomething){
		if (@this is Something<T> s){
			actionOnSomething(s.Value);
		}		
		return @this;
	}
	
    //<summary>Call method Bind first to get correct behavior;/summary>
	public static Maybe<T> OnNothing<T>(this Maybe<T> @this, Action actionOnNothing){
		if (@this is UnhandledNothing<T>){
			actionOnNothing();
			return new Nothing<T>(); //switch from UnhandledNothing<T> to Nothing<T>
		}
		return @this;
	}
	
    //<summary>Call method Bind first to get correct behavior;/summary>
	public static Maybe<T> OnError<T>(this Maybe<T> @this, Action<Exception> actionOnError){
		if (@this is UnhandledError<T> e){
			actionOnError(e.CapturedError);
			return new Error<T>(e.CapturedError); //switch from UnhandledError<T> to Error<T>
		}
		return @this;
	}
		
	/// <summary>Bind is similar to Map in functional programming, it applies a function to update and allow switching from TIn to TOut data types, possibly different types</summary>
	public static Maybe<TOut> Bind<TIn, TOut>(this Maybe<TIn> @this, Func<TIn, TOut> f){
		try
		{
			Maybe<TOut> updatedMaybe = @this switch {
			    null => new Error<TOut>(new Exception("Object input is null")),
				Something<TIn> s when !EqualityComparer<TIn>.Default.Equals(s.Value, default) => new Something<TOut>(f(s.Value)),
				Something<TIn> s when @this.GetType().GetGenericArguments().First().IsPrimitive && Nullable.GetUnderlyingType(@this.GetType()) == null => new Something<TOut>(f(s.Value)),
				Something<TIn> _ => new UnhandledNothing<TOut>(),
				UnhandledNothing<TIn> _ => new UnhandledNothing<TOut>(),
				Nothing<TIn> _ => new Nothing<TOut>(),
				UnhandledError<TIn> u => new UnhandledError<TOut>(u.CapturedError),
				Error<TIn> e => new Error<TOut>(e.CapturedError),
				_ => new Error<TOut>(new Exception($"Got a subtype of Maybe<T>, which is not supported: {@this?.GetType().Name}"))				
			};
			return updatedMaybe;			
		}
		catch (Exception ex)
		{			
			return new UnhandledError<TOut>(ex);
		}		
	}
		
}


The extension methods above handle the basics for the Maybe of T monad.
  • We transform from a value of T to the Maybe of T using the method ToMaybe, this method is called Return many places. I think the name ToMaybe is more intuitive for more developers in C#.
  • The method TryGetValue is called Reduce many places and extracts the value of Maybe of T if it is available and returns a boolean value if the Maybe of T actually contains a value. If Maybe of T is not the type Something of T it does not hold a value, so knowing this is useful after retrieving the value.
  • The method Bind is sometimes called Map and allows updates of the value inside the Maybe of T and also perform transitions of sub types of Maybe of T and uses pattern matching in C#. Bind both maps and also performs the overall flow of state via controlling the sub type of Maybe of T as shown in the pattern specified inside Bind
  • The methods OnError, OnNothing, OnSomething are callbacks to do logic when retrieving values of these types that inherit from Maybe of T. Make sure you call the method Bind first
If you know more methods a Maybe of T monad should support, please let me know. A more detailed example of a Maybe of T monad can be seen in this implementation, note that it is called Option of T instead.

https://github.com/nlkl/Optional/blob/master/src/Optional/Option_Maybe.cs

The benefit with the code in this article is that it is shorter and that it uses records in C#. Since it is shorter, it should be easier to adapt and adjust to the needs. Demo code is next. Consider this record:

Car.cs


public record Car(string Model, string Make, bool? IsFourWheelDrive = null, string? Color = null);




Program.cs


void Main()
{
	var something = new Something<string>("hullo");
	something = something with { Value = null };
	
	var somethingnull = EqualityComparer<string>.Default.Equals(something.Value, default);
	
	int? nullableNumber = 1;
	Maybe<int?> maybe = nullableNumber.ToMaybe();

	var volvo = new Car("240 GL", "Volvo");

	bool isValueRetrieved = volvo.ToMaybe()
		.Bind(car => car with { IsFourWheelDrive = false })
		.Bind(car => car with { Color = "Blue" })
		.TryGetValue(out Car updatedVolvo);

	new { updatedVolvo, isValueRetrieved }.Dump("Updated Volvo");
	
	Maybe<string> noCar = new Something<string>(null)
							.Bind(x => x)
							.OnNothing(() => Console.WriteLine("The Car is nothing (null)!"));
	noCar.Dump("No car");
		
	maybe.Dump("Maybe");
	
	something.Dump();
	
}



Output from Linqpad shows the result from running the demo code. Note that since a record Car was used in the demo code, inside the Bind calls it was necessary to use the 'with' to mutate the record and create a new record
with the necessary adjustments. Also, it is important to call Bind before handlers OnNothing, OnError and OnSomething to work properly in the code, since the transition rules inside Bind should run first to check the correct subtype of Maybe of T. Many implementations of Option of T or Maybe of T that is, also implement operators for equality comparison of the value inside the Maybe of T if there is a defined value there (i.e. Something of T). The mentioned example on Github goes in detail on this and adds several methods for equality comparisons. Also, there are examples of support async methods such as BindAsync in Maybe of T implementations out there. Many of the building blocks of functional programming in C# are not standardized or built into the framework, so working out these building blocks yourself to tailour your source code needs is probably a good solution, if not going for libraries such as LanguageExt.

https://github.com/louthy/language-ext

Finally, notice that in the screenshot below, we have a Value of null for the variable something in the demo code. To get the proper sub type of Maybe of T, we should also call the Bind method. If we instead of :

  something.Dump();

Do this:

  something.Bind(x => x).Dump();

We get the correct subtype of Maybe of T, UnhandledNothing of T.

And if we also handle the UnhandledNothing of T, we get Nothing of T. 

something.Bind(x => x).OnNothing(() => Console.WriteLine("something is null")).Dump();

Sunday, 30 June 2024

Fork combinator revisited - supporting multiple part functions in C#

This article shows an example of a Fork combinator or 'monad' that will allow you to specify a join function that operates on all the part results and allow you to specify multiple part functions to be operated in sequence. First off, we define a simple map monad to map a value to another value of possibly other type. Then we define the fork combinator. The code below is very simple and short, it uses LINQ functionality to combine the results via the Select method, Linq is also functional so this is how we build up functional monads in C#, using Linq and Func and generics (and pattern matching and more).

Combinators.cs


public static class Combinators {
	
	public static TOut Map<TIn, TOut>(this TIn @this, Func<TIn, TOut> f) => f(@this);
	
	public static TOut Fork<TIn, TMiddle, TOut>(this TIn @this, Func<IEnumerable<TMiddle>, TOut> joinFunc,
		params Func<TIn, TMiddle>[] partFuncs) => partFuncs.Select(pf => pf(@this)).Map(joinFunc);
	
}


Let's look at a simple demo how to use this

Program.cs


public static class Program {
	
	string hello = "hhhhhheeeeeeeelllllllllllooooo";
	
	int sumOfLettersToLookFor = hello.Fork(results => (int)results.Sum(), 
				x => (double)x.Count(l => l == 'h'),
				x => (double) x.Count(l => l == 'e'),
				x => (double) x.Count(l => l == 'l'),
				x => (double) x.Count(l => l == 'o'));
	
	sumOfLettersToLookFor.Dump();
	
}


Functional programming has many of these monads that are very short and allows you to do combinations that would be lengthy and stateful in the procedural / object oriented way but elegant and short in the functional world. Finally a screenshot from Linqpad 7 showing the code above works : (A reference to Jerry Seinfeld to the right for those who know Seinfeld episodes)

Iterating a list using Span in C# - Benchmark

It is possible to iterate a list in C# using Span. This article shows a benchmark on that and how you can create a Span of a list. It is important to note that the Span will despite it is not doing any memory to memory copying and is performance optimized, it will not
detect changes done to the list after the span is created, therefore the iterations you perform should not be done on a list where you expect that the list is altered during the iteration using the Span. Let's first look at the Benchmark, BenchmarkDotNet will be used. The Github repo with the source code for the benchmark is here: https://github.com/toreaurstadboss/BenchmarkIterateCollections The program is started up using BenchmarkRunner
Program.cs


using BenchmarkDotNet.Running;

namespace BenmarkIterateCollections;


public class Program
{

    public static void Main(string[] args)
    {
        BenchmarkRunner.Run<IterateCollectionsBenchmark>();
        Console.WriteLine("Benchmark finished. Hit the any key to exit");
        Console.ReadKey();
    }
}



The benchmarks look like this, the base line is iterating an array.
BenchmarkIterateCollections.cs


using BenchmarkDotNet.Attributes;
using System.Runtime.InteropServices;

namespace BenmarkIterateCollections;

public class IterateCollectionsBenchmark
{

    private Random _rnd = new Random();
    private int[] _array = default!;

    private List<int> _list = default!;
    private const int ITEM_COUNT = 1001;

    [GlobalSetup]
    public void Init()
    {
        _array = Enumerable.Range(0, ITEM_COUNT).Select(_ => _rnd.Next(0, 1024)).ToArray();
        _list = _array.ToList();
    }

    [Benchmark(Description = "Iterate an array with foreach", Baseline = true)]
    public void IterateArray()
    {
        foreach (var item in _array)
        {
        }
    }

    [Benchmark(Description = "Iterate a list using a span with foreach")]
    public void IterateArrayUsingSpan()
    {
        Span<int> span = _array; //note : implicit operator used here to convert an array to a Span
        foreach (var item in span)
        {

        }
    }

    [Benchmark(Description = "Iterate a list with foreach")]
    public void IterateList()
    {
        foreach (var item in _list)
        {

        }
    }

    [Benchmark(Description = "Iterate a list using a span with foreach")]
    public void IterateListUsingSpan()
    {
        Span<int> span = CollectionsMarshal.AsSpan(_list);
        foreach (var item in span)
        {

        }
    }

}



Conclusions

Iterating an array using a span actually ran 6% faster than iterating an array without using the span. Iteating a list using a span ran some 50% faster than iterating the list using the span. The code above used the method CollectionsMarshal.AsSpan() in System.Runtime.InteropServices namespace - this has been available since .NET 5.x. Being aware of this method means we can iterate lists about 50% faster keeping in mind that changes to the list after the span was created from the list will not be shown in the span and we could show stale data if changes occur. You can also use this in .NET Framework using System.Memory Nuget package For large lists you know will not change, using spans to iterate could and should promise a large performance improvement. It is also possible to slice a portion of lists or arrays in case it is a case of iterating parts of a large list or array.