C# c中'this'和'ref'的等效功能#
如果我在一个类中有一个函数:C# c中'this'和'ref'的等效功能#,c#,.net,this,C#,.net,This,如果我在一个类中有一个函数: /* 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) {
/* 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
对象一起大量存在于代码中
我希望重构(在本例中,在reader对象上使用扩展方法会更好)
this
(外部类上的扩展方法)和ref
(函数之间的传递引用)在功能上是否等效
更新-引用此
必需???
作为更新,仅使用此
不起作用,在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
相比,
中的的进步在于,您无法修改参数,但无法获得副本(如“正常”参数)。您还可以在
参数中作为传递只读字段
private void Expect(in Utf8JsonReader reader, in JsonTokenType t)
{
reader.Read();
/* snip */
}
ref
正常。而ref此
相当于/另一种形式的
ExtensionClass.ExpectNamedProperty(参考读取器)
同时,不要在
中使用。
在这种情况下,
中的会降低性能
中的适用于只读结构
,而对于非只读结构,编译器每次使用该结构时都会创建防御副本,以确保该结构是只读的。这会显著降低性能
在您的例子中,是ref结构
,而不是只读结构
考虑这个例子:
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参数传递。它几乎总是会使性能变差
这
使得Expect
成为Utf8JsonReader
的扩展方法:您可以将reader.Expect(t)代码>那么。也许我误解了,但在第一个示例中,您是否需要ref
?如果您打算更改读取器本身的引用,是否真的需要这样做?(即reader=)@code陌生人这是一个结构,如果他使用System.Text.Json.Utf8JsonReader
,我会将它写为private void Expect(在Utf8JsonReader中,在JsonTokenType t中)
,这与ref
@code陌生人相同,我认为它是一个类,在这里也是一样。。我得先查一下,从来没用过。你为什么要用ref this
?正如其他人提到的,这使得它成为一种扩展方法。将其标记为ref
允许您更改指向的对象,这严重违反了for-an-extension方法。您可以同时执行Expect(这在Utf8JsonReader,JsonTokenType t中)
就像我说的,它真的与Utf8JsonReader密切相关吗?代码中的许多点都使用它吗?将它用作扩展方法的一个问题是,它不再支持继承。你不能推翻它。是的,我很感激这一点。有很多期望值略有不同,它们占据了全班的大部分。在这种情况下,将它们放在扩展方法中会使实际使用扩展的类更加清晰。in
不能在Utf8JsonReader
上使用。我发布了一个答案来解释原因。在这种情况下,部分类是相关的,因为使用ref和这一起需要修改项目(这是必需的),但其他任何开发人员都不清楚,因此是危险的。分部类至少使它明确地清除了整个中的关键字只不过是一个伪解决方案,而不是一个实体“不创建副本”并通过引用传递。只读。我在sharplab上做了一些测试,我在调用方法时看到了防御副本。只有在访问这些字段时,才会创建副本。