クラス内に関数がある場合:
/* class snipped */
private void Expect(ref Utf8JsonReader reader, JsonTokenType t)
{
reader.Read();
/* snip */
}
オブジェクトが操作されているため、参照によって渡されるのは、静的ヘルパー関数とは異なります。
/*static class snipped*/
static public void Expect(this Utf8JsonReader reader, JsonTokenType t)
{
reader.Read();
/* snip */
}
// call helper via reader.Expect(requiredToken)
ref
が使用されているときに目に見えないニュアンスが発生した場合は、ネストされた関数間で渡されるUtf8JsonReader
およびMemory<T>
オブジェクトとともにコードに大きく含まれています。
私はリファクタリングを探しています(この場合、リーダーオブジェクトで拡張メソッドを使用するとはるかにうまくいきます)。
this
(外部クラスの拡張メソッド)とref
(関数間の参照渡し)は機能的に同等ですか?
更新-ref this
が必要ですか??
更新として、単にthis
を使用しても機能しませんでした。ExpectNamedProperty
関数内ではreader.Expect
を呼び出しますが、オブジェクトを返すと元に戻ります。どういうわけか、スタックにコピーが作成されているか、何かが進行中です。
これが有効な組み合わせであることも知りませんでした。ref this
は機能しますが、this
は変更されません。ひどいことをしていないことを明確にする必要があります!
public static void Expect(ref this Utf8JsonReader reader, JsonTokenType t)
{
reader.Read(); // this mutation is never passed back to caller
}
public static void ExpectNamedProperty(ref this Utf8JsonReader reader, string expectedPropertyName)
{
reader.Expect(JsonTokenType.PropertyName, expectedPropertyName);
// at this point it is as if the function above was never called
var foundPropertyName = reader.GetString();
if (foundPropertyName != StreamFieldNames.Event)
throw new JsonException($"expected {StreamFieldNames.Event} found {foundPropertyName} at position {reader.Position.GetInteger()}");
}
ref
で結構です。そしてref this
は/の別の形式と同等です
ExtensionsClass.ExpectNamedProperty(参照リーダー)
in
は使用しないでください。この場合、in
はパフォーマンスを低下させます。
in
はreadonly struct
に対して完全に機能しますが、読み取り専用でない構造体の場合、コンパイラは防御コピーを作成します毎回構造体は構造体が読み取り専用であることを確認するために使用されます。これにより、パフォーマンスが大幅に低下します。
あなたの場合、 Utf8JsonReader
はref struct
であり、readonly struct
ではありません。
この例を考えてみましょう:
private void ExpectWithIn(in Utf8JsonReader reader)
{
reader.Read();
}
private void ExpectWithRef(ref Utf8JsonReader reader)
{
reader.Read();
}
ExpectWithRef(ref reader);
ExpectWithIn(reader);
ExpectWithRef
のコンパイルされたIL:
// (no C# code)
IL_0000: nop
// reader.Read();
IL_0001: ldarg.1
IL_0002: call instance bool [System.Text.Json]System.Text.Json.Utf8JsonReader::Read()
IL_0007: pop
// (no C# code)
IL_0008: ret
ExpectWithIn
のコンパイルされたIL:
// (no C# code)
IL_0000: nop
// The compiler creates defensive copy to make sure reader variable is readonly
// The compiler repeats this for every use of reader variable
// so this is significant impact
// Utf8JsonReader utf8JsonReader = reader;
IL_0001: ldarg.1
IL_0002: ldobj [System.Text.Json]System.Text.Json.Utf8JsonReader
IL_0007: stloc.0
// utf8JsonReader.Read();
IL_0008: ldloca.s 0
IL_000a: call instance bool [System.Text.Json]System.Text.Json.Utf8JsonReader::Read()
IL_000f: pop
// (no C# code)
IL_0010: ret
Sergey Tepliakovは 良い記事 でin
修飾子とそれをいつ使うべきかを説明しています。
つまり、パラメーターのように読み取り専用ではない構造体を渡してはいけません。ほぼ常にパフォーマンスが低下します。