如果我在一个类中有一个函数。
/* 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(ref reader)
in
.in
在这种情况下,会使性能变差。
in
对于 readonly struct
而对于非readonly结构体,编译器会创建防御性副本 每次 的结构是用来确保该结构是只读的。这将大大降低性能。
在你的情况下。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);
汇编的IL ExpectWithRef
:
// (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
汇编的IL: ExpectWithIn
:
// (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
修饰符以及何时使用它。
这意味着你永远不应该在参数中传递一个非readonly结构。它几乎 始终会使性能变差.
你可以写很多扩展方法,但它们真的有关系吗?把所有的东西都写在扩展方法中,会产生意大利面条代码。
我会选择 in
关键字。的进步。in
在...上 ref
是你不能修改参数,但你不会得到一个副本(就像一个 "普通 "参数)。你也可以将只读字段作为 in
参数。
private void Expect(in Utf8JsonReader reader, in JsonTokenType t)
{
reader.Read();
/* snip */
}