using System.Collections.Generic;
using System.Text.Json.Serialization;
public class TypedValue<T>
public T? Value { get; init; }
public TypedValue(T? value) => Value = value;
public override string? ToString() => Value?.ToString() ?? string.Empty;
public override bool Equals(object? obj) =>
obj is TypedValue<T> other && EqualityComparer<T>.Default.Equals(Value, other.Value)
public override int GetHashCode() => Value?.GetHashCode() ?? default;
public static bool operator ==(TypedValue<T>? lhs, TypedValue<T>? rhs) => lhs?.Equals(rhs) ?? rhs is null;
public static bool operator !=(TypedValue<T>? lhs, TypedValue<T>? rhs) => !(lhs == rhs);
public class TypedValueConverter<T, V> : JsonConverter<T>
private readonly static JsonConverter<V> valueConverter = (JsonConverter<V>)new JsonSerializerOptions().GetConverter(typeof(V));
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
(T?)typeof(T).GetConstructors().Single().Invoke(new object?[] { valueConverter.Read(ref reader, typeToConvert, options) });
public override void Write(Utf8JsonWriter writer, T typedValue, JsonSerializerOptions options) =>
valueConverter.Write(writer, (V?)typeof(T).GetProperties().Single().GetValue(typedValue)!, options);
[JsonConverter(typeof(TypedValueConverter<UnitSymbol, string>))]
public sealed class UnitSymbol : TypedValue<string>
public UnitSymbol(string? Symbol) : base(Symbol) { }
[JsonConverter(typeof(TypedValueConverter<UnitName, string>))]
public sealed class UnitName : TypedValue<string>
public UnitName(string? Name) : base(Name) { }
public record struct UnitDto(
private static readonly JsonSerializerOptions SerializerOptions = new()
PropertyNameCaseInsensitive = true,
public static void Main()
var s1 = new UnitSymbol("mV");
var s2 = new UnitSymbol("mV");
var s3 = new UnitSymbol("mm");
var n1 = new UnitName("Millivolt");
var n2 = new UnitName("Millivolt");
var n3 = new UnitName("Millimeter");
Console.WriteLine(s1 == s2);
Console.WriteLine(s2 == s1);
Console.WriteLine(s1 != s3);
Console.WriteLine(s3 != s1);
Console.WriteLine(s1.Equals(s2));
Console.WriteLine(s2.Equals(s1));
Console.WriteLine(!s1.Equals(s3));
Console.WriteLine(!s3.Equals(s1));
Console.WriteLine(n1 == n2);
Console.WriteLine(n2 == n1);
Console.WriteLine(n1 != n3);
Console.WriteLine(n3 != n1);
Console.WriteLine(n1.Equals(n2));
Console.WriteLine(n2.Equals(n1));
Console.WriteLine(!n1.Equals(n3));
Console.WriteLine(!n3.Equals(n1));
var dto1_1 = new UnitDto(s1, n1);
var dto2_1 = new UnitDto(s2, n2);
var dto3_1 = new UnitDto(s3, n3);
Console.WriteLine("{0}", dto1_1);
Console.WriteLine("{0}", dto2_1);
Console.WriteLine("{0}", dto3_1);
var json1 = JsonSerializer.Serialize<UnitDto>(dto1_1, SerializerOptions);
var json2 = JsonSerializer.Serialize<UnitDto>(dto2_1, SerializerOptions);
var json3 = JsonSerializer.Serialize<UnitDto>(dto3_1, SerializerOptions);
Console.WriteLine("{0}", json1);
Console.WriteLine("{0}", json2);
Console.WriteLine("{0}", json3);
var dto1_2 = JsonSerializer.Deserialize<UnitDto>(json1, SerializerOptions);
var dto2_2 = JsonSerializer.Deserialize<UnitDto>(json2, SerializerOptions);
var dto3_2 = JsonSerializer.Deserialize<UnitDto>(json3, SerializerOptions);
Console.WriteLine("{0}", dto1_2);
Console.WriteLine("{0}", dto2_2);
Console.WriteLine("{0}", dto3_2);
Console.WriteLine("{0}", JsonSerializer.Serialize<UnitDto>(dto1_2, SerializerOptions));
Console.WriteLine("{0}", JsonSerializer.Serialize<UnitDto>(dto2_2, SerializerOptions));
Console.WriteLine("{0}", JsonSerializer.Serialize<UnitDto>(dto3_2, SerializerOptions));