Encoding.GetString()は救われない
Encoding.GetString()
は救われない
.NETで文字コードを扱う際、Encoding.GetString()
でString
に変換します。しかしこのメソッドは.NET 8.0.6現在、救われない実装になっていることが分かりました。
Encoding.GetString()
のオーバーロード
Encoding.GetString()
には4つのオーバーロードがありますので、それぞれ見ていきます。
Encoding.GetString(Byte[], Int32, Int32)
Encoding.GetString(Byte[], Int32, Int32)
は
- GetChars()を呼んでChar[]を作り、
- new String(Char[])を呼ぶ
となっているため、途中で無駄なChar[]
を作成してしまいます。
Encoding.GetString(Byte[])
- Encoding.GetString(Byte[], Int32, Int32)を呼ぶ
となっているため、先述の通りです。
Encoding.GetString(Byte*, Int32)
Encoding.GetString(Byte*, Int32)
は
- String.CreateStringFromEncoding(Byte*, Int32, Encoding)を呼ぶ
となっているため、この後、探っていきます。
Encoding.GetString(ReadOnlySpan<Byte>)
Encoding.GetString(ReadOnlySpan<Byte>)
は
- 固定してポインターを得て、
- String.CreateStringFromEncoding(Byte*, Int32, Encoding)を呼ぶ
となっているため、嫌な予感しかしませんが、先述の通りで、探っていきます。
Encoding.GetString()
が呼び出すメソッド
String.CreateStringFromEncoding(Byte*, Int32, Encoding)
String.CreateStringFromEncoding(Byte*, Int32, Encoding)
は
- Encoding.GetCharCount(Byte*, Int32)を呼んで長さを得て、
- String()領域を確保し、
- 固定してポインターを得て、
- Encoding.GetChars(Byte*, Int32, Char*, Int32)で書き込む
となっているため、Encoding.GetString(Byte[])
の時より好感が持てます。呼び出し先を更に探ります。
Encoding.GetCharCount(Byte*, Int32)
Encoding.GetCharCount(Byte*, Int32)
はまずvirtual
なので派生クラスでオーバーライド可能です。デフォルト実装は
- ポインターからReadOnlySpanを作成し
- ReadOnlySpan.ToArray()でByte[]を作り、
- Encoding.GetCharCount(Byte[], Int32, Int32)を呼ぶ
おいいいいいい…ここまできて用もなく配列を作りますか。
結論
Encoding.GetString()
のすべてのオーバーロードは変換途中で一時配列を作成しています。
おまけ… String(SByte*, Int32, Int32, Encoding)
コンストラクタがありますが、こちらもEncoding.GetString()
を呼ぶので同じようです。
よく使う、Shift-JISエンコーディングの場合、実体はDBCSCodePageEncoding
で、親クラスがBaseCodePageEncoding
で、その親クラスがEncodingNLS
になっています。
EncodingNLS
がEncoding.GetCharCount(Byte*, Int32)
をオーバーライドすることで、余計な配列を作らずに済むようになっているようです。
またUTF-8エンコーディングの場合も、実体はUTF8Encoding
で、こちらもEncoding.GetCharCount(Byte*, Int32)
をオーバーライドすることで、余計な配列を作らずに済むようになっています。
これら個別のエンコーディングについては、Encoding.GetString(Byte*, Int32)
かEncoding.GetString(ReadOnlySpan<Byte>)
を使えば一時配列を作らずにString
に変換できるようです。
Discussion
DBCSCodePageEncoding
もフォールバック処理か何かでnew byte[]
していて、そこはArrayPool
とか使えんのか、と思いました。