I would like to format byte[]
and ReadOnlySpan<byte>
bytes to strings using a few formatting parameters. Say, like S
for Base64
. For the purpose of this, the length is always fixed to some known constant.
I would like to use the modern C# 10 and .NET 6 string formatting capabilities as described at https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/.
I took some code from that post and modified it a bit in the code embedded as follows. It's also at https://dotnetfiddle.net/svyQKD.
As is seen in the code, I get the direct method call to succeed for byte[]
, but not for ReadOnlySpan<byte>
.
Does anyone have a clue how to do that?
I suspect I need InterpolatedStringHandler. But if that is the case, then it looks like I don't know how to implement one. All tips and code tricks would probably help. I've been stuck at this for a while now and it's getting into wee hours. :)
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
public class Program
{
public sealed class ExampleCustomFormatter: IFormatProvider, ICustomFormatter
{
public object? GetFormat(Type? formatType) => formatType == typeof(ICustomFormatter) ? this : null;
public string Format(string? format, object? arg, IFormatProvider? formatProvider) => format == "S" && arg is byte[] i ? Convert.ToBase64String(i) : arg is IFormattable formattable ? formattable.ToString(format, formatProvider) : arg?.ToString() ?? string.Empty;
}
public static class StringExtensions
{
public static string FormatString(byte[] buffer) => string.Create(new ExampleCustomFormatter(), stackalloc char[64], $"{buffer:S}");
// How to make this work? Maybe needs to have TryWrite
// public static string FormatString2(ReadOnlySpan<byte> buffer) => string.Create(new ExampleCustomFormatter(), stackalloc char[64], $"{buffer:S}");
}
[InterpolatedStringHandler]
public ref struct BinaryMessageInterpolatedStringHandler
{
private readonly DefaultInterpolatedStringHandler handler;
public BinaryMessageInterpolatedStringHandler(int literalLength, int formattedCount, bool predicate, out bool handlerIsValid)
{
handler = default;
if(predicate)
{
handlerIsValid = false;
return;
}
handlerIsValid = true;
handler = new DefaultInterpolatedStringHandler(literalLength, formattedCount);
}
public void AppendLiteral(string s) => handler.AppendLiteral(s);
public void AppendFormatted<T>(T t) => handler.AppendFormatted(t);
public override string ToString() => handler.ToStringAndClear();
}
public static void Main()
{
byte[] test1 = new byte[1] { 0x55 };
ReadOnlySpan<byte> test2 = new byte[1] { 0x55 };
// How to make this work? Now it prints "System.Byte[]".
Console.WriteLine($"{test1:S}");
// This works.
Console.WriteLine(StringExtensions.FormatString(test1));
// How to make this work? This does not compile. (Yes, signature problem. How to define it?).
// Console.WriteLine($"{test2:2}");
// How to make this work? This does not compile. (Yes, signature problem. How to define it?).
// Console.WriteLine(StringExtensions.FormatString(test2));
}
}
CodePudding user response:
You can use an extension method:
using System.Text;
byte[] test1 = new byte[2] { 0x55, 0x34 };
ReadOnlySpan<byte> test2 = new byte[2] { 0x55, 0x34 };
// How to make this work? Now it prints "System.Byte[]".
Console.WriteLine($"{test1.MyFormat()}");
Console.WriteLine($"{test2.MyFormat()}");
public static class MyExtensionMethods
{
public static string MyFormat(this byte[] value)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
{
sb.Append(b).Append(" ");
}
return sb.ToString();
}
public static string MyFormat(this ReadOnlySpan<byte> value)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in value)
{
sb.Append(b).Append(" ");
}
return sb.ToString();
}
}
Result:
85 52
85 52
CodePudding user response:
If you really want to use the method like this, you need to override the ToString() method of the class "Byte[]".
But you cannot override the method on the very class Byte[]. You need to inherit the class Byte[] and override the ToString() method on the derivated.
Then, you must replace all your Byte[] objects with the derivated class, with is not a good idea.
So, there is no solution for you in this manner:
// How to make this work? Now it prints "System.Byte[]".
Console.WriteLine($"{test1:S}");
The best you can do is create an "outside" method for formating the Byte[] and do as you with for formating.
*The same applies to the ReadOnlySpan