Just use ValueObjects instead of primitives. Just as JonasH already said.
Make a new class named "CaseInsensitiveString" that implements IEquatable. The record will then use the Equals method.
Something like this:
public readonly struct CaseInsensitiveString : IEquatable<CaseInsensitiveString>
{
public readonly String StringValue;
public CaseInsensitiveString( string value )
{
StringValue = value;
}
public bool Equals( CaseInsensitiveString other)
{
return string.Equals( StringValue, other.StringValue, StringComparison.InvariantCultureIgnoreCase );
}
public static implicit operator string( CaseInsensitiveString h ) => h.StringValue;
public static implicit operator CaseInsensitiveString( string s ) => new CaseInsensitiveString(s);
}
Because of the implicit operators you can just use strings:
record MyKey(CaseInsensitiveString Foo, int Bar);
...
var a = new MyKey("ID", 42 );
See also: https://wiki.c2.com/?PrimitiveObsession`