In an application that can write numeric values to a file using BinaryWriter
I have a class that is typed to the number type that should be used for the file. It looks like this:
class ValueCollection<T> where T : struct, IConvertible
{
private BinaryWriter writer;
}
The constructor will make sure that only supported types are used as type arguments. (Code not shown here, it's just typeof(T)
comparisons.)
It has a method that should write such a value to the file writer, that looks like this:
public void Write(T value)
{
if (typeof(T) == typeof(int)) writer.Write(value.ToInt32(NumberFormatInfo.InvariantInfo));
if (typeof(T) == typeof(long)) writer.Write(value.ToInt64(NumberFormatInfo.InvariantInfo));
if (typeof(T) == typeof(double)) writer.Write(value.ToDouble(NumberFormatInfo.InvariantInfo));
// (There are more...)
}
That's all fine. But how can I support the Half
type with this in an allocation-free and non-dynamic manner? This doesn't work:
if (typeof(T) == typeof(Half)) writer.Write((Half)value);
// Cannot convert T to System.Half
IConvertible
and the Convert
class don't know about Half
at all. Am I lost here with this shiny new data type being unusable when all other number types work just fine?
I'm targeting .NET 8 and can use all the features there are. But it needs to work in an AOT publishing environment, so no reflection and probably also no dynamics.
PS: The same will come for a read method that uses BinaryReader.ReadHalf
and should convert it back to T
to return a value.
You cannot immediately use System.Half
as type value T
, as Half
does not implement the IConvertible
interface that you request in the constraint.
Since you state that you check the number type in the constructor anyway, you could as a workaround drop that type constraint, and instead implement a custom handler function for every type. You then choose a suitable one during initialization:
class ValueCollection<T> where T : struct
{
private readonly BinaryWriter _writer;
private readonly Action<T, BinaryWriter> _writeValue;
public ValueCollection(BinaryWriter writer)
{
_writer = writer;
_writeValue = typeof(T) switch
{
Type t when t == typeof(int) => (Action<T, BinaryWriter>)(object)WriteInt32,
// ...
Type t when t == typeof(Half) => (Action<T, BinaryWriter>)(object)WriteHalf,
_ => throw new NotSupportedException()
};
}
public void Write(T value) => _writeValue(value, _writer);
private static void WriteInt32(int value, BinaryWriter writer) => writer.Write(value);
// ...
private static void WriteHalf(Half value, BinaryWriter writer) => writer.Write(value);
}