使用 C# 10 和 .NET 6 字符串插值、编译器处理程序降低模式将 byte[] 或 ReadOnlySpan<byte> 格式化为字符串
Formatting byte[] or ReadOnlySpan<byte> to string using C# 10 and .NET 6 string interpolation, compiler handler lowering pattern
我想使用一些自定义格式化参数将 byte[]
和 ReadOnlySpan<byte>
字节格式化为字符串。比如说 S
代表 Base64
。为此,长度始终固定为某个已知常数。
我想使用 https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/. The built-in types implement ISpanFormattable and so what I'd like to bring here is new formatting parameters but so that compiler handler lowering pattern 中描述的现代 C# 10 和 .NET 6 字符串格式化功能。
我从 post 中提取了一些代码,并在嵌入的代码中对其进行了一些修改,如下所示。它也在 https://dotnetfiddle.net/svyQKD.
正如在代码中看到的那样,我获得了 byte[]
的直接方法调用成功,但 ReadOnlySpan<byte>
没有。
有人知道怎么做吗?
我怀疑我需要 InterpolatedStringHandler。但如果是这样的话,那么我似乎不知道如何实施。所有提示和代码技巧都可能有所帮助。我已经坚持了一段时间,现在已经到了凌晨。 :)
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:S}");
// How to make this work? This does not compile. (Yes, signature problem. How to define it?).
// Console.WriteLine(StringExtensions.FormatString(test2));
}
}
如果你真的想使用这样的方法,你需要覆盖classByte[]
的ToString()
方法。
但是您不能在 class Byte[]
上覆盖该方法。您需要继承 class Byte[]
并覆盖派生的 ToString()
方法。
然后,您必须用派生的 class 替换所有 Byte[]
对象,这不是一个好主意。
所以,没有适合您的解决方案:
// How to make this work? Now it prints "System.Byte[]".
Console.WriteLine($"{test1:S}");
你能做的最好的事情是创建一个“外部”方法来格式化 Byte[]
并按照你的方式进行格式化。
*ReadOnlySpan<byte>
.
同理
您可以使用扩展方法:
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();
}
}
结果:
85 52
85 52
您也可以试试:
public static class MyExtensionMethods
{
public static string MyFormat(this byte[] value) => Encoding.Unicode.GetString(value);
public static string MyFormat(this ReadOnlySpan<byte> value) => Encoding.Unicode.GetString(value);
}
我想使用一些自定义格式化参数将 byte[]
和 ReadOnlySpan<byte>
字节格式化为字符串。比如说 S
代表 Base64
。为此,长度始终固定为某个已知常数。
我想使用 https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/. The built-in types implement ISpanFormattable and so what I'd like to bring here is new formatting parameters but so that compiler handler lowering pattern 中描述的现代 C# 10 和 .NET 6 字符串格式化功能。
我从 post 中提取了一些代码,并在嵌入的代码中对其进行了一些修改,如下所示。它也在 https://dotnetfiddle.net/svyQKD.
正如在代码中看到的那样,我获得了 byte[]
的直接方法调用成功,但 ReadOnlySpan<byte>
没有。
有人知道怎么做吗?
我怀疑我需要 InterpolatedStringHandler。但如果是这样的话,那么我似乎不知道如何实施。所有提示和代码技巧都可能有所帮助。我已经坚持了一段时间,现在已经到了凌晨。 :)
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:S}");
// How to make this work? This does not compile. (Yes, signature problem. How to define it?).
// Console.WriteLine(StringExtensions.FormatString(test2));
}
}
如果你真的想使用这样的方法,你需要覆盖classByte[]
的ToString()
方法。
但是您不能在 class Byte[]
上覆盖该方法。您需要继承 class Byte[]
并覆盖派生的 ToString()
方法。
然后,您必须用派生的 class 替换所有 Byte[]
对象,这不是一个好主意。
所以,没有适合您的解决方案:
// How to make this work? Now it prints "System.Byte[]".
Console.WriteLine($"{test1:S}");
你能做的最好的事情是创建一个“外部”方法来格式化 Byte[]
并按照你的方式进行格式化。
*ReadOnlySpan<byte>
.
您可以使用扩展方法:
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();
}
}
结果:
85 52
85 52
您也可以试试:
public static class MyExtensionMethods
{
public static string MyFormat(this byte[] value) => Encoding.Unicode.GetString(value);
public static string MyFormat(this ReadOnlySpan<byte> value) => Encoding.Unicode.GetString(value);
}